From 7e2e3c16e8493657c4e385e174fded32cc14f379 Mon Sep 17 00:00:00 2001 From: Darks Date: Thu, 21 Apr 2016 09:42:44 +0200 Subject: [PATCH] Added MonochromeLib and its documentation --- Documentation/MonochromeLib_doc_en.html | 725 +++++++++++++ Documentation/MonochromeLib_doc_fr.html | 725 +++++++++++++ MonochromeLib.c | 1286 +++++++++++++++++++++++ MonochromeLib.h | 151 +++ 4 files changed, 2887 insertions(+) create mode 100644 Documentation/MonochromeLib_doc_en.html create mode 100644 Documentation/MonochromeLib_doc_fr.html create mode 100755 MonochromeLib.c create mode 100644 MonochromeLib.h diff --git a/Documentation/MonochromeLib_doc_en.html b/Documentation/MonochromeLib_doc_en.html new file mode 100644 index 0000000..431c6d6 --- /dev/null +++ b/Documentation/MonochromeLib_doc_en.html @@ -0,0 +1,725 @@ + + + + MonochromeLib - Documentation + + + + +
+

MonochromeLib, what is it ?

+

+ MonochromeLib is a graphic library for Casio fx-9860G SDK.
+ It provides fast and efficient drawing functions to developpers.
+ Every functions of MonochromeLib are significantly faster than its counterpart from fxlib.h, and this library provides lot of others functionnalities. +

+
+
+

How to use

+

To use this library, copy the 2 files in your project directory, add MonochromeLib.c in your project (in window "Files in project" in SDK), add #include "MonochromeLib.h" at the beginning of your code.

+

To add in project only the functions you need, each function is protected by an #ifdef, and the #define of each function is commented by default.

+

To use a function, just edit MonochromeLib.h and uncomment #define of functions you want to use.

+

+ /!\ Important
+ If you encounter a building error like :
+ ** L2310 (E) Undefined external symbol "_ML_pixel" referenced in "C:\...\CASIO\fx-9860G SDK\Projet\Debug\MonochromeLib.obj"
+ and if the #define of the function is well uncommented in MonochromeLib.h, then you just have to rebuild MonochromeLib.c
+ Use the function Project > Rebuilt all in SDK. If it not solved the problem, delete Debug directory of your project, and rebuild normally. +

+
+
+

Documentation of functions

+ +
+

ML_vram_adress

+

char* ML_vram_adress();

+

+ Returns the VRAM adress (which is different on OS 1, OS 2, and emulator).
+ VRAM is a video memory, a buffer of 1024 bytes made to receive drawings before to be copied on screen, on the double-buffering principle.
+
+ This function is not really useful for a normal use of MonochromeLib, but it's useful for all others functions of the lib. +

+ +
+
+

ML_clear_vram

+

void ML_clear_vram();

+

+ Clears the VRAM.
+ This function is 5 times faster than Bdisp_AllClr_VRAM. +

+ +
+
+

ML_clear_screen

+

void ML_clear_screen();

+

+ Clears the screen.
+ This function is 2 times faster than Bdisp_AllClr_DD.
+ Note : It's not necessary to call ML_clear_screen just before ML_display_vram. +

+ +
+
+

ML_display_vram

+

void ML_display_vram();

+

+ Copies VRAM content to screen.
+ This function is 2 times faster than Bdisp_PutDisp_DD.
+ Note : It's not necessary to call ML_clear_screen just before ML_display_vram. +

+ +
+
+

ML_set_contrast

+

void ML_set_contrast(unsigned char contrast);

+

+ Set contrast value.
+ This value have to be between ML_CONTRAST_MIN and ML_CONTRAST_MAX. +

+ +
+
+

ML_get_contrast

+

unsigned char ML_get_contrast();

+

+ Returns actual contrast value. +

+ +
+
+

ML_pixel

+

void ML_pixel(int x, int y, ML_Color color);

+

+ Set the color of a dot in VRAM.
+ The upper left pixel have for coordinate (x=0, y=0), and the bottom right (x=127, y=63). +

+ +
+
+

ML_point

+

void ML_point(int x, int y, int width, ML_Color color);

+

+ Draws a point (square) in VRAM, centered at (x, y), with sides lenght (in pixel) are defined by parameter width.
+ Example: +

ML_point(10, 10, 3, ML_BLACK);

+ will draw a black rectangle from (9, 9) to (11, 11).
+ + +

+ +
+
+

ML_pixel_test

+

ML_Color ML_pixel_test(int x, int y);

+

+ Returns the color of the pixel in coordinates (x, y), ML_BLACK or ML_WHITE.
+ If coordinates are out of screen, the function return ML_TRANSPARENT. +

+ +
+
+

ML_line

+

void ML_line(int x1, int y1, int x2, int y2, ML_Color color);

+

Draws a line between points in coordinates (x1, y1) and (x2, y2) using Bresenham algorithm.

+ +
+
+

ML_horizontal_line

+

void ML_horizontal_line(int y, int x1, int x2, ML_Color color);

+

+ Draws a horizontal line.
+ This function is faster than a call to ML_line with y1==y2. +

+ +
+
+

ML_vertical_line

+

void ML_vertical_line(int x, int y1, int y2, ML_Color color);

+

+ Draws a vertical line.
+ This function is faster than a call to ML_line with x1==x2. +

+ +
+
+

ML_rectangle

+

void ML_rectangle(int x1, int y1, int x2, int y2, int border_width, ML_Color border_color, ML_Color fill_color);

+

+ Draws a rectangle with or without border.
+ You can define the border color, and the fill color.
+ If you want no border, set border_width to 0. +

+ +
+
+

ML_polygon

+

void ML_polygon(const int *x, const int *y, int nb_vertices, ML_Color color);

+

+ Draws a polygon.
+ This function needs as parameters 2 arrays, containing abscissa and ordinates of the polygon vertices. Parameter nb_vertices should be the number of data to read in arrays.
+ This function draws a line between each vertices of the polygon.
+
+ Example : +

+ int abscissa[] = {60, 75, 70, 50, 45};
+ int ordinate[] = {20, 30, 45, 45, 30};
+ ML_clear_vram();
+ ML_polygon(abscissa, ordinate, 5, ML_BLACK);
+ ML_display_vram(); +

+ Well done, a "pretty" pentagon in the middle of screen.
+ + +

+ +
+
+

ML_filled_polygon

+

void ML_filled_polygon(const int *x, const int *y, int nb_vertices, ML_Color color);

+

Similar to ML_polygon, but draws filled polygon.

+ +
+
+

ML_circle

+

void ML_circle(int x, int y, int radius, ML_Color color);

+

Draws a circle centered on (x, y) using Bresenham algorithm.

+ +
+
+

ML_filled_circle

+

void ML_filled_circle(int x, int y, int radius, ML_Color color);

+

Similar to ML_circle, but draws filled circle..

+ +
+
+

ML_ellipse

+

void ML_ellipse(int x, int y, int radius1, int radius2, ML_Color color);

+

+ Draws an ellipse centered on (x, y) with radiuses radius1 et radius2. radius1 is distance between center and lefmost point of ellipse, radius2 is distance between center and upper point of ellipse. Use the Bresenham algorithm. +

+ +
+
+

ML_ellipse_in_rect

+

void ML_ellipse_in_rect(int x1, int y1, int x2, int y2, ML_Color color);

+

This function calls ML_ellipse. It expect rectangle coordinates, and draw an ellipse in this rectangle..

+ +
+
+

ML_filled_ellipse

+

void ML_filled_ellipse(int x, int y, int radius1, int radius2, ML_Color color);

+

Similar to ML_ellipse, but draws a filled ellipse.

+ +
+
+

ML_filled_ellipse_in_rect

+

void ML_filled_ellipse_in_rect(int x, int y, int radius1, int radius2, ML_Color color);

+

Similar to ML_ellipse_in_rect, but draws a filled ellipse.

+ +
+
+

ML_horizontal_scroll

+

void ML_horizontal_scroll(int scroll);

+

+ Shifts all pixels in VRAM to left or right. For example, if scroll=5, then a pixel on (2, 3) will be moved on (7, 3). If scroll is a negative value, pixels will be shift to left. When pixels reach screen boundaries, they reappear on the other side. +

+ +
+
+

ML_vertical_scroll

+

void ML_vertical_scroll(int scroll);

+

Similar to ML_horizontal_scroll, but scroll vertically.

+ +
+
+

ML_bmp...

+

+ void ML_bmp_or(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_and(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_xor(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_or_cl(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_and_cl(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_xor_cl(const unsigned char *bmp, int x, int y, int width, int height);
+
+ void ML_bmp_8_or(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_and(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_xor(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_or_cl(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_and_cl(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_xor_cl(const unsigned char *bmp, int x, int y);
+
+ void ML_bmp_16_or(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_and(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_xor(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_or_cl(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_and_cl(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_xor_cl(const unsigned short *bmp, int x, int y); +

+

+ These functions are made to draw images in monochrome bitmap format. They are very useful to draw tiles and sprites in games.
+
+ Functions with prefix ML_bmp_8 are used to draw 8*8 sized bitmap.
+ Functions with prefix ML_bmp_16 are used to draw 16*16 sized bitmap.
+ Others expect dimensions of the bitmap in width and height parameters.
+
+ Functions with suffix _cl are with clipping. They can draw the bitmap even if it's not totally in screen.
+ Others draw the bitmap only if it's totally in screen. As a result, they are a little faster. +

+ +
+
+
+

Constants

+ +
+

ML_Color

+

typedef enum {ML_TRANSPARENT=-1, ML_WHITE, ML_BLACK, ML_XOR, ML_CHECKER} ML_Color;

+

+ ML_Color is an enumeration of the different colors usable in MonochromeLib.
+ Only ML_TRANSPARENT is set, with a value of -1, so the compiler give to others the following values : +

    +
  • ML_TRANSPARENT = -1
  • +
  • ML_WHITE = 0
  • +
  • ML_BLACK = 1
  • +
  • ML_XOR = 2
  • +
  • ML_CHECKER = 3
  • +
+ ML_XOR reverses actual color of the pixel in VRAM.
+ ML_CHECKER is a "checkerboard" color. It makes black 1 pixel on 2, and white the other, according the following rule :
+ if (x and y are even) or (x and y are odd), then the pixel becomes black, else it becomes white.
+ Example: +

ML_rectangle(50, 20, 80, 40, 2, ML_BLACK, ML_CHECKER);

+ + +

+
+
+

ML_SCREEN_WIDTH et ML_SCREEN_HEIGHT

+

+ These constants spécify the screen size for which MonochromeLib is made.
+

    +
  • ML_SCREEN_WIDTH = 128
  • +
  • ML_SCREEN_HEIGHT = 64
  • +
+ These constants are not used by the library, and change will not affect its behavior. MonochromeLib isn't made for working with other screen size. +

+ +
+
+

ML_CONTRAST_MIN, ML_CONTRAST_NORMAL et ML_CONTRAST_MAX

+

+ These constants spécify the minimum and maximum values accepted by fx-9860G as contrast.
+

    +
  • ML_CONTRAST_MIN = 130
  • +
  • ML_CONTRAST_NORMAL = 168
  • +
  • ML_CONTRAST_MAX = 190
  • +
+

+ +
+
+
+

Tutoriels

+ +
+

The VRAM

+

+ VRAM (abbreviation of Video RAM) is a buffer created by the operating system.
+ It allows to practice double-buffering.
+
+ Screen of the fx-9860G contains 128*64 = 8192 pixels. It's a monochrome screen, that means each pixel have 2 possible states, ON or OFF (black or white). + Therefore a pixel can be stored in one bit (0 ou 1). A byte is consisting of 8 bits, so we can store all pixels of the screen in 1024 bytes (8192/8 = 1024). + The VRAM is thus a buffer of 1024 bytes.
+
+ One function in operating system (a syscall) return the VRAM adress in memory. With this adress, we can write and read in VRAM, for draw, or analyze the content. It's this function which is called in ML_vram_adress.
+ At the beginning of fx-9860G programming, this function was unknown, although we known the VRAM adress : 0x8800498D. But this adress have changed with release 2.0 of the operating system, making several programs ineffective. So, the use of this syscall is necessary to guarantee the good working of programs on every operating system version.
+
+ The VRAM is organized as follows: + + + + + +
0123456789101112131415
16171819202122232425262728293031
...
1008100910101011101210131014101510161017101810191020102110221023
+ the "0" box is the first byte of VRAM, the "1023" box is the last.
+
+ Example of handling : +

memset(ML_vram_adress(), 255, 1024);

+ This line will copy the value 255 in the 1024 bytes of VRAM. 255 in binary is written 11111111, only 1. So this line fill VRAM with black. We can then call ML_display_vram and the screen will be all black. +

+ +
+
+

Double buffering

+

+ Double buffering is a technique of computer graphics. The principle is to make all drawing operations in a video memory (VRAM) before to copy it on the real screen. This allows not to display an image under construction and prevents screen flicker.
+
+ At first, clear the VRAM content with ML_clear_vram, next do your drawings, and finally display the VRAM content on screen with ML_display_vram. +

+ +
+
+

Bitmap

+

+ A bitmap is a data array in which each bit account for one pixel. +

Create a 8*8 bitmap

+ A pixel is either black or white, so we can store its state in one bit (0 or 1). A byte is consisting of 8 bits, therefore 8 pixels. For a 8*8 bitmap, each line fits in a byte, so we need 8 bytes, one per line.
+ Take an example, the picture of a ball : +

+ 00111100 -> 60
+ 01111110 -> 126
+ 11111011 -> 251
+ 11111101 -> 253
+ 11111101 -> 253
+ 11111111 -> 255
+ 01111110 -> 126
+ 00111100 -> 60 +

+ Here, I taken binary numbers of each line of the picture, and I converted them in decimal.
+ This gives us the following array : +

char ball[] = {60, 126, 251, 253, 253, 255, 126, 60};

+ This array is a 8*8 bitmap that can be used with ML_bmp functions to draw it. +

Create a bitmap of any dimensions

+ Now we know how 8*8 bitmaps are encoded, it gonna be easy to understand the management of other sizes.
+ For an 16*16 bitmap, we have 16 bits per line, 2 bytes.
+ 2 bytes per line multiplied by 16 lines, gives us 32 bytes for a 16*16 bitmap.
+ The first two are the first line, the next two are the second line, etc.
+
+ For a bitmap whose width is less than 8, each line still take a byte. The last bits of each byte are simply unused.
+ For a bitmap whose width is greater than 8, but non multiple of 8, it will be the same, the last bits of the last byte of each line will be unused.
+ The ML_bmp functions apply a mask on these last bits to disable them, regardless of content. So if I take the ball bitmap created previously, and I draw it this way : +

ML_bmp_or(ball, x, y, 4, 8);

+ Only the left half will be drawn. +

The different drawing modes: OR, AND, XOR

+ Take for example the ball bitmap created previously, and draw it in the 3 different modes on black and white background : +

+ char ball[] = {60, 126, 251, 253, 253, 255, 126, 60};
+
+ ML_rectangle(1, 1, 12, 32, 0, ML_TRANSPARENT, ML_BLACK); //black background
+
+ //draw on black background
+ ML_bmp_8_or(ball, 3, 3);
+ ML_bmp_8_and(ball, 3, 13);
+ ML_bmp_8_xor(ball, 3, 23);
+
+ //draw on white background
+ ML_bmp_8_or(ball, 15, 3);
+ ML_bmp_8_and(ball, 15, 13);
+ ML_bmp_8_xor(ball, 15, 23); +

+ +
+
+ The ML_bmp_or functions make a binary OR between bitmap and VRAM bits.
+ Reminder :
+ 0 OR 0 = 0
+ 0 OR 1 = 1
+ 1 OR 0 = 1
+ 1 OR 1 = 1
+ So a bitmap drawing in OR mode will copy black pixels of the bitmap and let others like "transparent".
+
+ The AND mode will copy only white pixels of the bitmap.
+ With XOR mode, the black pixels of the bitmap will reverse the color of VRAM pixels, and the white pixels of the bitmap have no effect.
+
+ To overwrite VRAM pixels without transparency, draw the bitmap with AND, then with OR.
+ If you have a sprite with black, white and transparent pixels, you need 2 bitmaps. The first containing 0 on white pixels to be applied with AND, the second containing 1 on black pixels to be applied with OR. Pixels at 1 in first and 0 in second, will be transparent.
+ If you want the black pixels of the bitmap to be copied in white in VRAM, you must apply the same bitmap with OR then with XOR.
+ So, with these 3 modes, you can do what you want.
+
+

Software to encode bitmap

+ There is a lot of software which can generate bitmap in this format. + + You can also make your own !
+ It's not really hard, you can program in C, and you now know how bitmaps are encoded. +

+ +
+
+ + + diff --git a/Documentation/MonochromeLib_doc_fr.html b/Documentation/MonochromeLib_doc_fr.html new file mode 100644 index 0000000..33208e6 --- /dev/null +++ b/Documentation/MonochromeLib_doc_fr.html @@ -0,0 +1,725 @@ + + + + MonochromeLib - Documentation + + + + +
+

MonochromeLib, qu'est ce que c'est ?

+

+ MonochromeLib est une bibliothèque de dessin pour le SDK Casio Graph 85.
+ Elle fournit aux développeurs des fonctions optimisées pour tracer toute sorte de choses à l'écran.
+ Chaque fonction de MonochromeLib est bien plus rapide que son équivalent dans fxlib.h, et elle fournit de nombreuses fonctionnalités supplémentaires. +

+
+
+

Comment l'utiliser

+

Pour utiliser la bibliothèque, copiez les 2 fichiers dans le dossier de votre projet, ajoutez MonochromeLib.c à votre projet (dans la fenêtre "Files in project" dans le SDK), ajoutez #include "MonochromeLib.h" au début de votre code.

+

Pour n'ajouter à votre projet que les fonctions dont vous avez besoin, chaque fonction est protégée par un #ifdef, et les #define de chaque fonction sont commentés par défaut.

+

Pour pouvoir utiliser une fonction, il suffit d'éditer MonochromeLib.h et de décommenter les #define des fonctions que vous voulez utiliser.

+

+ /!\ Important
+ Si vous rencontrez une erreur de compilation de ce type :
+ ** L2310 (E) Undefined external symbol "_ML_pixel" referenced in "C:\...\CASIO\fx-9860G SDK\Projet\Debug\MonochromeLib.obj"
+ et que le #define de la fonction en question est bien actif dans MonochromeLib.h, alors il faut juste recompiler MonochromeLib.c
+ Pour cela, Utilisez la fonction Project > Rebuilt all dans le SDK. Si cela ne résoud pas le problème, supprimez le dossier Debug de votre projet, et recompilez normalement. +

+
+
+

Documentation des fonctions

+ +
+

ML_vram_adress

+

char* ML_vram_adress();

+

+ Retourne l'adresse de la VRAM (celle ci est différente sur l'OS 1, l'OS 2, et l'émulateur).
+ La VRAM est la mémoire vidéo, un espace mémoire de 1024 octets conçue pour recevoir les dessins avant d'être affichée à l'écran, selon le principe du double-buffering.
+
+ Cette fonction n'est pas forcément utile pour une utilisation classique de MonochromeLib, mais elle est utile à toutes les fonctions de la bibliothèque. +

+ +
+
+

ML_clear_vram

+

void ML_clear_vram();

+

+ Efface la VRAM.
+ Cette fonction est 5 fois plus rapide que Bdisp_AllClr_VRAM. +

+ +
+
+

ML_clear_screen

+

void ML_clear_screen();

+

+ Efface l'écran.
+ Cette fonction est 2 fois plus rapide que Bdisp_AllClr_DD.
+ Remarque : Il est inutile d'appeler ML_clear_screen juste avant ML_display_vram. +

+ +
+
+

ML_display_vram

+

void ML_display_vram();

+

+ Copie le contenu de la VRAM à l'écran.
+ Cette fonction est 2 fois plus rapide que Bdisp_PutDisp_DD.
+ Remarque : Il est inutile d'appeler ML_clear_screen juste avant ML_display_vram. +

+ +
+
+

ML_set_contrast

+

void ML_set_contrast(unsigned char contrast);

+

+ Permet de définir la valeur du contraste.
+ Celle ci doit être comprise entre ML_CONTRAST_MIN et ML_CONTRAST_MAX. +

+ +
+
+

ML_get_contrast

+

unsigned char ML_get_contrast();

+

+ Retourne la valeur actuelle du contraste. +

+ +
+
+

ML_pixel

+

void ML_pixel(int x, int y, ML_Color color);

+

+ Permet de définir la couleur d'un pixel de la VRAM.
+ Le pixel en haut à gauche de l'écran a pour coordonnées (x=0, y=0), et le pixel en bas à droite (x=127, y=63). +

+ +
+
+

ML_point

+

void ML_point(int x, int y, int width, ML_Color color);

+

+ Dessine un point (carré) dans la VRAM, de centre (x, y), dont la longueur des coté (en pixel) est définie par width.
+ Exemple: +

ML_point(10, 10, 3, ML_BLACK);

+ dessinera un rectangle noir allant de (9, 9) à (11, 11).
+ + +

+ +
+
+

ML_pixel_test

+

ML_Color ML_pixel_test(int x, int y);

+

+ Retourne la couleur du pixel aux coordonnées (x, y), ML_BLACK ou ML_WHITE.
+ Si les coordonnées sont en dehors de l'écran, la fonction retourne ML_TRANSPARENT. +

+ +
+
+

ML_line

+

void ML_line(int x1, int y1, int x2, int y2, ML_Color color);

+

Trace une ligne entre les pixels de coordonnées (x1, y1) et (x2, y2) en utilisant l'algorithme de Bresenham.

+ +
+
+

ML_horizontal_line

+

void ML_horizontal_line(int y, int x1, int x2, ML_Color color);

+

+ Dessine une ligne horizontale.
+ Cette fonction est plus rapide qu'un appel à ML_line avec y1==y2. +

+ +
+
+

ML_vertical_line

+

void ML_vertical_line(int x, int y1, int y2, ML_Color color);

+

+ Dessine une ligne verticale.
+ Cette fonction est plus rapide qu'un appel à ML_line avec x1==x2. +

+ +
+
+

ML_rectangle

+

void ML_rectangle(int x1, int y1, int x2, int y2, int border_width, ML_Color border_color, ML_Color fill_color);

+

+ Dessine un rectangle avec ou sans bordure.
+ Vous pouvez définir la couleur de la bordure et du remplissage du rectangle.
+ Si vous ne voulez pas de bordure, définissez border_width à 0. +

+ +
+
+

ML_polygon

+

void ML_polygon(const int *x, const int *y, int nb_vertices, ML_Color color);

+

+ Dessine un polygone.
+ Cette fonction demande en paramètre deux tableaux d'entiers, le premier contenant les abscisses des sommets du polygone, et le second contenant les ordonnées. Le paramètre nb_vertices doit être le nombre de sommets du polygone (le nombre de valeurs à lire dans les tableaux x et y).
+ Ensuite, la fonction trace des lignes entre ces sommets pour dessiner le polygone.
+
+ Exemple : +

+ int abscisses[] = {60, 75, 70, 50, 45};
+ int ordonnees[] = {20, 30, 45, 45, 30};
+ ML_clear_vram();
+ ML_polygon(abscisses, ordonnees, 5, ML_BLACK);
+ ML_display_vram(); +

+ Et voilà un "joli" pentagone au milieu de l'écran.
+ + +

+ +
+
+

ML_filled_polygon

+

void ML_filled_polygon(const int *x, const int *y, int nb_vertices, ML_Color color);

+

Demande les mêmes paramètres que ML_polygon, mais dessine un polygon plein.

+ +
+
+

ML_circle

+

void ML_circle(int x, int y, int radius, ML_Color color);

+

Trace un cercle de centre (x, y) et de rayon radius en utilisant l'algorithme de Bresenham.

+ +
+
+

ML_filled_circle

+

void ML_filled_circle(int x, int y, int radius, ML_Color color);

+

Similaire à ML_circle, mais dessine un cercle plein.

+ +
+
+

ML_ellipse

+

void ML_ellipse(int x, int y, int radius1, int radius2, ML_Color color);

+

+ Trace une ellipse de centre (x, y) et de rayons radius1 et radius2. radius1 est la distance entre le centre et les points les plus à gauche et à droite de l'ellipse. radius2 est la distance entre le centre et les points les plus haut et bas de l'ellipse. Utilise l'algorithme de Bresenham. +

+ +
+
+

ML_ellipse_in_rect

+

void ML_ellipse_in_rect(int x1, int y1, int x2, int y2, ML_Color color);

+

Cette fonction appelle ML_ellipse. Elle demande les coordonnées d'un rectangle, et trace l'ellipse inscrite dans ce rectangle.

+ +
+
+

ML_filled_ellipse

+

void ML_filled_ellipse(int x, int y, int radius1, int radius2, ML_Color color);

+

Similaire à ML_ellipse, mais dessine une ellipse pleine.

+ +
+
+

ML_filled_ellipse_in_rect

+

void ML_filled_ellipse_in_rect(int x, int y, int radius1, int radius2, ML_Color color);

+

Similaire à ML_ellipse_in_rect, mais dessine une ellipse pleine.

+ +
+
+

ML_horizontal_scroll

+

void ML_horizontal_scroll(int scroll);

+

+ Permet de décaler tous les pixels de la VRAM vers la gauche ou la droite. Par exemple, si scroll=5, alors un pixel situé en (2, 3) sera déplacé en (7, 3). Si scroll est négatif, les pixels seront déplacés vers la gauche. Les pixels qui sortent de l'écran sont replacés de l'autre cotés. +

+ +
+
+

ML_vertical_scroll

+

void ML_vertical_scroll(int scroll);

+

Similaire à ML_horizontal_scroll, mais effectue le décalage verticalement.

+ +
+
+

ML_bmp...

+

+ void ML_bmp_or(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_and(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_xor(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_or_cl(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_and_cl(const unsigned char *bmp, int x, int y, int width, int height);
+ void ML_bmp_xor_cl(const unsigned char *bmp, int x, int y, int width, int height);
+
+ void ML_bmp_8_or(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_and(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_xor(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_or_cl(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_and_cl(const unsigned char *bmp, int x, int y);
+ void ML_bmp_8_xor_cl(const unsigned char *bmp, int x, int y);
+
+ void ML_bmp_16_or(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_and(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_xor(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_or_cl(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_and_cl(const unsigned short *bmp, int x, int y);
+ void ML_bmp_16_xor_cl(const unsigned short *bmp, int x, int y); +

+

+ Toutes ces fonctions servent à dessiner des images au format de bitmap monochrome. Elles sont très utiles pour dessiner les tiles ou sprites des jeux.
+
+ Les fonction avec le préfixe ML_bmp_8 servent à dessiner des bitmaps de dimensions 8*8.
+ Les fonction avec le préfixe ML_bmp_16 servent à dessiner des bitmaps de dimensions 16*16.
+ Les autres demandent les dimensions du bitmap dans les paramètres width et height
+
+ Les fonction avec le suffixe _cl sont les fonctions avec clipping. C'est à dire qu'elle sont capables de dessiner un bitmap même s'il n'est pas totalement dans l'écran.
+ Les autres n'affiche le bitmap que s'il est entièrement dans l'écran. De ce fait, elles sont un petit peu plus rapides. +

+ +
+
+
+

Constantes

+ +
+

ML_Color

+

typedef enum {ML_TRANSPARENT=-1, ML_WHITE, ML_BLACK, ML_XOR, ML_CHECKER} ML_Color;

+

+ ML_Color est une énumération des différentes couleurs utilisables avec MonochromeLib.
+ Seul ML_TRANSPARENT a une valeur définie à -1, le compilateur donne donc aux autres les valeurs suivantes : +

    +
  • ML_TRANSPARENT = -1
  • +
  • ML_WHITE = 0
  • +
  • ML_BLACK = 1
  • +
  • ML_XOR = 2
  • +
  • ML_CHECKER = 3
  • +
+ ML_XOR permet d'inverser la couleur déjà présente dans la VRAM.
+ ML_CHECKER est une couleur "damier". Elle rend 1 pixel sur 2 blanc, et l'autre noir, selon la rêgle suivante :
+ si (x et y sont pair) ou (x et y sont impairs), alors le pixel devient noir, sinon il devient blanc.
+ Exemple: +

ML_rectangle(50, 20, 80, 40, 2, ML_BLACK, ML_CHECKER);

+ + +

+
+
+

ML_SCREEN_WIDTH et ML_SCREEN_HEIGHT

+

+ Ces constantes définissent la taille de l'écran pour laquelle MonochromeLib est faite pour fonctionner.
+

    +
  • ML_SCREEN_WIDTH = 128
  • +
  • ML_SCREEN_HEIGHT = 64
  • +
+ Ces constantes ne sont pas utilisées par la librairie, et leur modification n'affectera en rien son fonctionnement. MonochromeLib n'est pas conçue pour fonctionner avec un écran d'une autre dimension. +

+ +
+
+

ML_CONTRAST_MIN, ML_CONTRAST_NORMAL et ML_CONTRAST_MAX

+

+ Ces constantes définissent les valeurs de contraste minimum et maximum acceptées par une Graph 85.
+

    +
  • ML_CONTRAST_MIN = 130
  • +
  • ML_CONTRAST_NORMAL = 168
  • +
  • ML_CONTRAST_MAX = 190
  • +
+

+ +
+
+
+

Tutoriels

+ +
+

La VRAM

+

+ La VRAM (abréviation de Vidéo RAM) est une mémoire vidéo créée par le système d'exploitation.
+ Elle permet de mettre en oeuvre la technique du double-buffering.
+
+ L'écran de la Graph 85 fait 128*64 pixels, soit 8192. C'est un écran monochrome, ce qui signifie que chaque pixel a 2 états possibles, allumé ou éteint (noir ou blanc). + On peut donc stocker l'état d'un pixel en mémoire dans un bit (0 ou 1). Comme un octet contient 8 bits, on peut stocker l'état de l'ensemble des pixels de l'écran dans 1024 octets (8192/8 = 1024). + La VRAM est en effet un espace mémoire de 1024 octets.
+
+ Il existe une fonction du système d'exploitation (un syscall) qui permet de connaître l'adresse de la VRAM dans la mémoire. Une fois que l'on a cette adresse, on peut accéder à la VRAM en lecture et en écriture, pour y faire des dessins, ou analyser son contenu. C'est cette fonction qui est appelée par ML_vram_adress.
+ Au début de la programmation sur Graph 85, cette fonction n'était pas connue, mais nous connaissions l'adresse de la VRAM : 0x8800498D. Mais cette adresse a été modifée dans la version 2 du système d'exploitation, rendant plusieurs programmes inopérants. L'utilisation de ce syscall est donc nécessaire pour garantir le fonctionnement du programme sur toutes les versions du système d'exploitation.
+
+ La VRAM est organisée de la manière suivante : + + + + + +
0123456789101112131415
16171819202122232425262728293031
...
1008100910101011101210131014101510161017101810191020102110221023
+ la case "0" est le premier octet de la VRAM, la case "1023" le dernier octet.
+
+ Exemple de manipulation : +

memset(ML_vram_adress(), 255, 1024);

+ Cette ligne va copier la valeur 255 dans les 1024 octets de la VRAM. 255 en binaire s'écrit 11111111, que des 1. Cette instruction sert donc à remplir la VRAM de noir. Il suffit ensuite d'appeler ML_display_vram et l'écran sera tout noir. +

+ +
+
+

Double buffering

+

+ La technique du double-buffering consiste à faire les dessins dans une mémoire vidéo (VRAM) avant de copier celle-ci sur l'écran réel. Cela permet de ne pas afficher une image "en construction" et évite le scintillement de l'écran.
+
+ Il faut commencer par effacer le contenu de la VRAM avec ML_clear_vram, puis faire les dessins, et enfin afficher le contenu de la VRAM à l'écran avec ML_display_vram. +

+ +
+
+

Bitmap

+

+ Un bitmap est un tableau de donnée dans lequel un bit représente un pixel. +

Créer un bitmap 8*8

+ Un pixel étant soit noir soit blanc, on peut stocker son état dans un bit (0 ou 1). Un octet contient 8 bits, on peut donc y stocker 8 pixels. Pour un bitmap 8*8, chaque ligne va tenir dans un octet, on va donc avoir 8 octets, un par ligne.
+ Prenons un exemple, l'image d'une balle : +

+ 00111100 -> 60
+ 01111110 -> 126
+ 11111011 -> 251
+ 11111101 -> 253
+ 11111101 -> 253
+ 11111111 -> 255
+ 01111110 -> 126
+ 00111100 -> 60 +

+ Ici, j'ai pris les nombres binaires correspondant à chaque ligne de l'image, et je les ai converti en décimal.
+ Nous obtenons donc le tableau suivant : +

char balle[] = {60, 126, 251, 253, 253, 255, 126, 60};

+ Ce tableau est un bitmap 8*8 que l'on peut envoyer aux fonctions ML_bmp pour le dessiner. +

Créer un bitmap de n'importe quelle taille

+ Maintenant que l'on sait comment sont codés les bitmaps 8*8, il va être simple de comprendre la gestion des autres dimensions.
+ Pour un bitmap 16*16, nous avons 16 bits par ligne, soit 2 octets.
+ 2 octets par ligne multiplié par 16 lignes, nous donne 32 octets pour un bitmap 16*16.
+ Les 2 premiers sont la première ligne, les 2 suivant, la seconde ligne, etc.
+
+ Pour un bitmap de largeur intérieure à 8, chaque ligne va tout de même prendre 1 octet. Les derniers bits de chaque octets seront simplement inutilisés.
+ Pour un bitmap d'une largeur supérieure à 8, mais non multiple de 8, ce sera pareil, les derniers bits du dernier octets de chaque ligne seront inutilisés.
+ Les fonctions ML_bmp appliquent un masque sur ces derniers bits pour qu'ils soient inactifs, quelque soit leur contenu. Ainsi, si je prends le bitmap balle créé précédemment, et que je le dessine de la manière suivante : +

ML_bmp_or(balle, x, y, 4, 8);

+ Seule la moitié de gauche sera dessinée. +

Les différents modes de dessin: OR, AND, XOR

+ Prenons comme exemple le bitmap de la balle créé précédemment, et dessinons-le des 3 manières différentes sur fond noir et blanc : +

+ char balle[] = {60, 126, 251, 253, 253, 255, 126, 60};
+
+ ML_rectangle(1, 1, 12, 32, 0, ML_TRANSPARENT, ML_BLACK); //dessin du fond noir
+
+ //dessin sur fond noir
+ ML_bmp_8_or(balle, 3, 3);
+ ML_bmp_8_and(balle, 3, 13);
+ ML_bmp_8_xor(balle, 3, 23);
+
+ //dessin sur fond blanc
+ ML_bmp_8_or(balle, 15, 3);
+ ML_bmp_8_and(balle, 15, 13);
+ ML_bmp_8_xor(balle, 15, 23); +

+ +
+
+ Les fonctions ML_bmp_or font un OR binaire entre les pixels de l'écran, et ceux du bitmap.
+ Pour rappel :
+ 0 OR 0 = 0
+ 0 OR 1 = 1
+ 1 OR 0 = 1
+ 1 OR 1 = 1
+ Donc l'application d'un bitmap en OR va copier les pixels noirs du bitmap et laisser les autres comme "transparent".
+
+ Le mode AND va copier uniquement les pixels blancs du bitmap.
+ Avec le mode XOR, les pixels noirs du bitmap vont inverser la couleur des pixels de l'écran, et les pixels blancs du bitmap seront sans effet.
+
+ Pour copier complètement le bitmap à l'écran sans transparence, il faut donc l'appliquer en AND puis en OR.
+ Si vous avez un sprite avec des pixels noirs, blancs, et transparents, il faut 2 bitmaps. Un premier contenant des 0 aux pixels blancs à appliquer en AND, et un second contenant des 1 aux pixels noirs à appliquer en OR. Les pixels à 1 dans le premier bitmap et à 0 dans le second, seront transparents.
+ Si vous voulez que les pixels noirs du bitmap soient copiés en blanc sur l'écran, il faut appliquer le bitmap en OR puis en XOR.
+ Bref, avez ces 3 modes de dessin, toutes les combinaisons sont possibles.
+
+

Logiciels d'encodage de bitmap

+ Il existe de nombreux logiciels pour encoder les bitmaps dans ce format. + + Vous pouvez également faire le votre !
+ Ce n'est pas très compliqué, vous savez programmer en C, et vous connaissez maintenant l'encodage des bitmaps. +

+ +
+
+ + + diff --git a/MonochromeLib.c b/MonochromeLib.c new file mode 100755 index 0000000..f6b9720 --- /dev/null +++ b/MonochromeLib.c @@ -0,0 +1,1286 @@ +/*************************************************************/ +/** MonochromeLib - monochrome graphic library for fx-9860G **/ +/** MonochromeLib is free software **/ +/** MonochromeLib is now SH4 compatible ! **/ +/** **/ +/** @author Pierre "PierrotLL" Le Gall **/ +/** @contact legallpierre89@gmail.com **/ +/** **/ +/** @file MonochromeLib.c **/ +/** Code file of MonochromeLib **/ +/** **/ +/** @date 11-22-2011 **/ +/*************************************************************/ + +#include "MonochromeLib.h" + +/******************************/ +/** Dependencies management **/ +/******************************/ + +#ifdef ML_ALL + #define ML_CLEAR_VRAM + #define ML_CLEAR_SCREEN + #define ML_DISPLAY_VRAM + #define ML_SET_CONTRAST + #define ML_GET_CONTRAST + #define ML_PIXEL + #define ML_POINT + #define ML_PIXEL_TEST + #define ML_LINE + #define ML_HORIZONTAL_LINE + #define ML_VERTICAL_LINE + #define ML_RECTANGLE + #define ML_POLYGON + #define ML_FILLED_POLYGON + #define ML_CIRCLE + #define ML_FILLED_CIRCLE + #define ML_ELLIPSE + #define ML_ELLIPSE_IN_RECT + #define ML_FILLED_ELLIPSE + #define ML_FILLED_ELLIPSE_IN_RECT + #define ML_HORIZONTAL_SCROLL + #define ML_VERTICAL_SCROLL + #define ML_BMP_OR + #define ML_BMP_AND + #define ML_BMP_XOR + #define ML_BMP_OR_CL + #define ML_BMP_AND_CL + #define ML_BMP_XOR_CL + #define ML_BMP_8_OR + #define ML_BMP_8_AND + #define ML_BMP_8_XOR + #define ML_BMP_8_OR_CL + #define ML_BMP_8_AND_CL + #define ML_BMP_8_XOR_CL + #define ML_BMP_16_OR + #define ML_BMP_16_AND + #define ML_BMP_16_XOR + #define ML_BMP_16_OR_CL + #define ML_BMP_16_AND_CL + #define ML_BMP_16_XOR_CL +#endif + +#ifdef ML_POLYGON + #define ML_LINE +#endif + +#ifdef ML_LINE + #define ML_PIXEL +#endif + +#ifdef ML_POINT + #define ML_PIXEL + #define ML_RECTANGLE +#endif + +#ifdef ML_RECTANGLE + #define ML_HORIZONTAL_LINE +#endif + +#ifdef ML_FILLED_POLYGON + #define ML_HORIZONTAL_LINE +#endif + +#ifdef ML_CIRCLE + #define ML_PIXEL +#endif + +#ifdef ML_FILLED_CIRCLE + #define ML_HORIZONTAL_LINE +#endif + +#ifdef ML_ELLIPSE_IN_RECT + #define ML_ELLIPSE +#endif + +#ifdef ML_ELLIPSE + #define ML_PIXEL +#endif + +#ifdef ML_FILLED_ELLIPSE_IN_RECT + #define ML_FILLED_ELLIPSE +#endif + +#ifdef ML_FILLED_ELLIPSE + #define ML_HORIZONTAL_LINE +#endif + + +/***************/ +/** Functions **/ +/***************/ + +#define sgn(x) (x<0?-1:1) +#define rnd(x) ((int)(x+0.5)) + +//Thanks to Simon Lothar for this function +typedef char*(*sc_cpv)(void); +const unsigned int sc0135[] = { 0xD201D002, 0x422B0009, 0x80010070, 0x0135 }; +#define ML_vram_adress (*(sc_cpv)sc0135) + + +#ifdef ML_CLEAR_VRAM +void ML_clear_vram() +{ + int i, end, *pointer_long, vram; + char *pointer_byte; + vram = (int)ML_vram_adress(); + end = 4-vram&3; + pointer_byte = (char*)vram; + for(i=0 ; i>3)] |= 128>>(x&7); + break; + case ML_WHITE: + vram[(y<<4)+(x>>3)] &= ~(128>>(x&7)); + break; + case ML_XOR: + vram[(y<<4)+(x>>3)] ^= 128>>(x&7); + break; + case ML_CHECKER: + if(y&1^x&1) vram[(y<<4)+(x>>3)] &= ~(128>>(x&7)); + else vram[(y<<4)+(x>>3)] |= 128>>(x&7); + break; + } +} +#endif + +#ifdef ML_POINT +void ML_point(int x, int y, int width, ML_Color color) +{ + if(width < 1) return; + if(width == 1) ML_pixel(x, y, color); + else + { + int padding, pair; + padding = width>>1; + pair = !(width&1); + ML_rectangle(x-padding+pair, y-padding+pair, x+padding, y+padding, 0, 0, color); + } +} +#endif + +#ifdef ML_PIXEL_TEST +ML_Color ML_pixel_test(int x, int y) +{ + char *vram, byte; + if(x&~127 || y&~63) return ML_TRANSPARENT; + vram = ML_vram_adress(); + byte = 1<<(7-(x&7)); + return (vram[(y<<4)+(x>>3)] & byte ? ML_BLACK : ML_WHITE); + +} +#endif + +#ifdef ML_LINE +void ML_line(int x1, int y1, int x2, int y2, ML_Color color) +{ + int i, x, y, dx, dy, sx, sy, cumul; + x = x1; + y = y1; + dx = x2 - x1; + dy = y2 - y1; + sx = sgn(dx); + sy = sgn(dy); + dx = abs(dx); + dy = abs(dy); + ML_pixel(x, y, color); + if(dx > dy) + { + cumul = dx / 2; + for(i=1 ; i dx) + { + cumul -= dx; + y += sy; + } + ML_pixel(x, y, color); + } + } + else + { + cumul = dy / 2; + for(i=1 ; i dy) + { + cumul -= dy; + x += sx; + } + ML_pixel(x, y, color); + } + } +} +#endif + +#ifdef ML_HORIZONTAL_LINE +void ML_horizontal_line(int y, int x1, int x2, ML_Color color) +{ + int i; + char checker; + char* vram = ML_vram_adress(); + if(y&~63 || (x1<0 && x2<0) || (x1>127 && x2>127)) return; + if(x1 > x2) + { + i = x1; + x1 = x2; + x2 = i; + } + if(x1 < 0) x1 = 0; + if(x2 > 127) x2 = 127; + switch(color) + { + case ML_BLACK: + if(x1>>3 != x2>>3) + { + vram[(y<<4)+(x1>>3)] |= 255 >> (x1&7); + vram[(y<<4)+(x2>>3)] |= 255 << 7-(x2&7); + for(i=(x1>>3)+1 ; i>3 ; i++) + vram[(y<<4) + i] = 255; + } + else vram[(y<<4)+(x1>>3)] |= (255>>(x1%8 + 7-x2%8))<<(7-(x2&7)); + break; + case ML_WHITE: + if(x1>>3 != x2>>3) + { + vram[(y<<4)+(x1>>3)] &= 255 << 8-(x1&7); + vram[(y<<4)+(x2>>3)] &= 255 >> 1+(x2&7); + for(i=(x1>>3)+1 ; i>3 ; i++) + vram[(y<<4) + i] = 0; + } + else vram[(y<<4)+(x1>>3)] &= (255<<8-(x1&7)) | (255>>1+(x2&7)); + break; + case ML_XOR: + if(x1>>3 != x2>>3) + { + vram[(y<<4)+(x1>>3)] ^= 255 >> (x1&7); + vram[(y<<4)+(x2>>3)] ^= 255 << 7-(x2&7); + for(i=(x1>>3)+1 ; i<(x2>>3) ; i++) + vram[(y<<4) + i] ^= 255; + } + else vram[(y<<4)+(x1>>3)] ^= (255>>((x1&7) + 7-(x2&7)))<<(7-(x2&7)); + break; + case ML_CHECKER: + checker = (y&1 ? 85 : 170); + if(x1>>3 != x2>>3) + { + vram[(y<<4)+(x1>>3)] &= 255 << 8-(x1&7); + vram[(y<<4)+(x2>>3)] &= 255 >> 1+(x2&7); + vram[(y<<4)+(x1>>3)] |= checker & 255>>(x1&7); + vram[(y<<4)+(x2>>3)] |= checker & 255<<7-(x2&7); + for(i=(x1>>3)+1 ; i>3 ; i++) + vram[(y<<4) + i] = checker; + } + else + { + vram[(y<<4)+(x1>>3)] &= (255<<8-(x1&7)) | (255>>1+(x2&7)); + vram[(y<<4)+(x1>>3)] |= checker & (255>>(x1%8 + 7-x2%8))<<(7-(x2&7)); + } + break; + } +} + +#endif + +#ifdef ML_VERTICAL_LINE +void ML_vertical_line(int x, int y1, int y2, ML_Color color) +{ + int i, j; + char checker, byte, *vram = ML_vram_adress(); + if(x&~127 || (y1<0 && y2<0) || (y1>63 && y2>63)) return; + if(y1 > y2) + { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + if(y1 < 0) y1 = 0; + if(y2 > 63) y2 = 63; + + i = (y1<<4)+(x>>3); + j = (y2<<4)+(x>>3); + switch(color) + { + case ML_BLACK: + byte = 128>>(x&7); + for( ; i<=j ; i+=16) + vram[i] |= byte; + break; + case ML_WHITE: + byte = ~(128>>(x&7)); + for( ; i<=j ; i+=16) + vram[i] &= byte; + break; + case ML_XOR: + byte = 128>>(x&7); + for( ; i<=j ; i+=16) + vram[i] ^= byte; + break; + case ML_CHECKER: + byte = 128>>(x&7); + checker = y1&1^x&1; + for( ; i<=j ; i+=16) + { + if(checker) vram[i] &= ~byte; + else vram[i] |= byte; + checker = !checker; + } + break; + } +} +#endif + +#ifdef ML_RECTANGLE +void ML_rectangle(int x1, int y1, int x2, int y2, int border_width, ML_Color border_color, ML_Color fill_color) +{ + int i; + if(x1 > x2) + { + i = x1; + x1 = x2; + x2 = i; + } + if(y1 > y2) + { + i = y1; + y1 = y2; + y2 = i; + } + if(border_width > (x2-x1)/2+1) border_width = (x2-x1)/2+1; + if(border_width > (y2-y1)/2+1) border_width = (y2-y1)/2+1; + if(border_color != ML_TRANSPARENT && border_width > 0) + { + for(i=0 ; i t[i]) + { + j++; + tmp = t[j]; + t[j] = t[i]; + t[i] = tmp; + } + } + t[r] = t[j+1]; + t[j+1] = x; + return j + 1; +} + +static void ML_filled_polygon_quicksord(int* t, int p, int r) +{ + int q; + if(p < r) + { + q = ML_filled_polygon_quicksord_partition(t, p, r); + ML_filled_polygon_quicksord(t, p, q-1); + ML_filled_polygon_quicksord(t, q+1, r); + } +} + + +void ML_filled_polygon(const int *x, const int *y, int nb_vertices, ML_Color color) +{ + int i, j, dx, dy, ymin, ymax; + int *cut_in_line, nb_cut; + if(nb_vertices < 3) return; + cut_in_line = malloc(nb_vertices*sizeof(int)); + if(!cut_in_line) return; + ymin = ymax = y[0]; + for(i=1 ; i ymax) ymax = y[i]; + } + for(i=ymin ; i<=ymax ; i++) + { + nb_cut = 0; + for(j=0 ; j=i) || (y[j]>=i && y[(j+1)%nb_vertices]<=i)) + { + dy = abs(y[j]-y[(j+1)%nb_vertices]); + if(dy) + { + dx = x[(j+1)%nb_vertices]-x[j]; + cut_in_line[nb_cut] = x[j] + rnd(abs(i-y[j]+sgn(i-y[j])/2)*dx/dy); + nb_cut++; + } + } + } + ML_filled_polygon_quicksord(cut_in_line, 0, nb_cut-1); + j = 0; + while(j plot_x) + { + if(d < 0) + d += 2*plot_x+3; + else + { + d += 2*(plot_x-plot_y)+5; + plot_y--; + } + plot_x++; + if(plot_y >= plot_x) + { + ML_pixel(x+plot_x, y+plot_y, color); + ML_pixel(x-plot_x, y+plot_y, color); + ML_pixel(x+plot_x, y-plot_y, color); + ML_pixel(x-plot_x, y-plot_y, color); + } + if(plot_y > plot_x) + { + ML_pixel(x+plot_y, y+plot_x, color); + ML_pixel(x-plot_y, y+plot_x, color); + ML_pixel(x+plot_y, y-plot_x, color); + ML_pixel(x-plot_y, y-plot_x, color); + } + } +} +#endif + +#ifdef ML_FILLED_CIRCLE +void ML_filled_circle(int x, int y, int radius, ML_Color color) +{ + int plot_x, plot_y, d; + + if(radius < 0) return; + plot_x = 0; + plot_y = radius; + d = 1 - radius; + + ML_horizontal_line(y, x-plot_y, x+plot_y, color); + while(plot_y > plot_x) + { + if(d < 0) + d += 2*plot_x+3; + else { + d += 2*(plot_x-plot_y)+5; + plot_y--; + ML_horizontal_line(y+plot_y+1, x-plot_x, x+plot_x, color); + ML_horizontal_line(y-plot_y-1, x-plot_x, x+plot_x, color); + } + plot_x++; + if(plot_y >= plot_x) + { + ML_horizontal_line(y+plot_x, x-plot_y, x+plot_y, color); + ML_horizontal_line(y-plot_x, x-plot_y, x+plot_y, color); + } + } +} +#endif + +#ifdef ML_ELLIPSE +void ML_ellipse(int x, int y, int radius1, int radius2, ML_Color color) +{ + int plot_x, plot_y; + float d1, d2; + if(radius1 < 1 || radius2 < 1) return; + plot_x = 0; + plot_y = radius2; + d1 = radius2*radius2 - radius1*radius1*radius2 + radius1*radius1/4; + ML_pixel(x, y+plot_y, color); + ML_pixel(x, y-plot_y, color); + while(radius1*radius1*(plot_y-.5) > radius2*radius2*(plot_x+1)) + { + if(d1 < 0) + { + d1 += radius2*radius2*(2*plot_x+3); + plot_x++; + } else { + d1 += radius2*radius2*(2*plot_x+3) + radius1*radius1*(-2*plot_y+2); + plot_x++; + plot_y--; + } + ML_pixel(x+plot_x, y+plot_y, color); + ML_pixel(x-plot_x, y+plot_y, color); + ML_pixel(x+plot_x, y-plot_y, color); + ML_pixel(x-plot_x, y-plot_y, color); + } + d2 = radius2*radius2*(plot_x+.5)*(plot_x+.5) + radius1*radius1*(plot_y-1)*(plot_y-1) - radius1*radius1*radius2*radius2; + while(plot_y > 0) + { + if(d2 < 0) + { + d2 += radius2*radius2*(2*plot_x+2) + radius1*radius1*(-2*plot_y+3); + plot_y--; + plot_x++; + } else { + d2 += radius1*radius1*(-2*plot_y+3); + plot_y--; + } + ML_pixel(x+plot_x, y+plot_y, color); + ML_pixel(x-plot_x, y+plot_y, color); + if(plot_y > 0) + { + ML_pixel(x+plot_x, y-plot_y, color); + ML_pixel(x-plot_x, y-plot_y, color); + } + } +} +#endif + +#ifdef ML_ELLIPSE_IN_RECT +void ML_ellipse_in_rect(int x1, int y1, int x2, int y2, ML_Color color) +{ + int radius1, radius2; + if(x1 > x2) + { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + if(y1 > y2) + { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + radius1 = (x2-x1)/2; + radius2 = (y2-y1)/2; + ML_ellipse(x1+radius1, y1+radius2, radius1, radius2, color); +} +#endif + +#ifdef ML_FILLED_ELLIPSE +void ML_filled_ellipse(int x, int y, int radius1, int radius2, ML_Color color) +{ + int plot_x, plot_y; + float d1, d2; + if(radius1 < 1 || radius2 < 1) return; + plot_x = 0; + plot_y = radius2; + d1 = radius2*radius2 - radius1*radius1*radius2 + radius1*radius1/4; + while(radius1*radius1*(plot_y-.5) > radius2*radius2*(plot_x+1)) + { + if(d1 < 0) + { + d1 += radius2*radius2*(2*plot_x+3); + plot_x++; + } else { + d1 += radius2*radius2*(2*plot_x+3) + radius1*radius1*(-2*plot_y+2); + ML_horizontal_line(y+plot_y, x-plot_x, x+plot_x, color); + ML_horizontal_line(y-plot_y, x-plot_x, x+plot_x, color); + plot_x++; + plot_y--; + } + } + ML_horizontal_line(y+plot_y, x-plot_x, x+plot_x, color); + ML_horizontal_line(y-plot_y, x-plot_x, x+plot_x, color); + d2 = radius2*radius2*(plot_x+.5)*(plot_x+.5) + radius1*radius1*(plot_y-1)*(plot_y-1) - radius1*radius1*radius2*radius2; + while(plot_y > 0) + { + if(d2 < 0) + { + d2 += radius2*radius2*(2*plot_x+2) + radius1*radius1*(-2*plot_y+3); + plot_y--; + plot_x++; + } else { + d2 += radius1*radius1*(-2*plot_y+3); + plot_y--; + } + ML_horizontal_line(y+plot_y, x-plot_x, x+plot_x, color); + if(plot_y > 0) + ML_horizontal_line(y-plot_y, x-plot_x, x+plot_x, color); + } +} +#endif + +#ifdef ML_FILLED_ELLIPSE_IN_RECT +void ML_filled_ellipse_in_rect(int x1, int y1, int x2, int y2, ML_Color color) +{ + int radius1, radius2; + if(x1 > x2) + { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + if(y1 > y2) + { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + radius1 = (x2-x1)/2; + radius2 = (y2-y1)/2; + ML_filled_ellipse(x1+radius1, y1+radius2, radius1, radius2, color); +} +#endif + +#ifdef ML_HORIZONTAL_SCROLL +void ML_horizontal_scroll(int scroll) +{ + int i, j; + char line[16], shift, *vram; + unsigned char next; + unsigned short word; + vram = ML_vram_adress(); + scroll %= 128; + shift = 8-(scroll&7); + for(i=0 ; i<64 ; i++) + { + for(j=0 ; j<16 ; j++) line[j] = vram[(i<<4)+((j-(scroll>>3)+15)&15)]; + next = line[15]; + vram[(i<<4)+15] = 0; + for(j=15 ; j>0 ; j--) + { + word = next << shift; + next = line[j-1]; + vram[(i<<4)+j] |= *((char*)&word+1); + vram[(i<<4)+j-1] = *((char*)&word); + } + word = next << shift; + vram[(i<<4)] |= *((char*)&word+1); + vram[(i<<4)+15] |= *((char*)&word); + } +} +#endif + +#ifdef ML_VERTICAL_SCROLL +void ML_vertical_scroll(int scroll) +{ + int i, j; + char column[64], *vram = ML_vram_adress(); + scroll %= 64; + for(i=0 ; i<16 ; i++) + { + for(j=0 ; j<64 ; j++) column[j] = vram[(j<<4)+i]; + for(j=0 ; j<64 ; j++) vram[(j<<4)+i] = column[(j-scroll+64)&63]; + } +} +#endif + +#ifdef ML_BMP_OR +void ML_bmp_or(const unsigned char *bmp, int x, int y, int width, int height) +{ + unsigned short line; + char shift, *screen, *p=(char*)&line; + int i, j, begin=0, end=height, real_width=(width-1>>3<<3)+8; + if(!bmp || x<0 || x>128-width || y<1-height || y>63 || width<1 || height<1) return; + if(y < 0) begin = -y; + if(y+height > 64) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i>3 ; j++) + { + line = bmp[i*(real_width>>3)+j]<>3)+j] & -1<<(real_width-width))<>3<<3)+8; + if(!bmp || x<0 || x>128-width || y<1-height || y>63 || width<1 || height<1) return; + if(y < 0) begin = -y; + if(y+height > 64) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i>3 ; j++) + { + line = ~((unsigned char)~bmp[i*(real_width>>3)+j]<>3)+j] | (unsigned char)-1>>8-(width&7))<>3<<3)+8; + if(!bmp || x<0 || x>128-width || y<1-height || y>63 || width<1 || height<1) return; + if(y < 0) begin = -y; + if(y+height > 64) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i>3 ; j++) + { + line = bmp[i*(real_width>>3)+j]<>3)+j] & -1<<(real_width-width))<127 || y<1-height || y>63 || height<1 || width<1) return; + p = (char*)&line; + real_width = (width-1>>3<<3)+8; + if(y < 0) begin_y = -y; + else begin_y = 0; + if(y+height > 64) end_y = 64-y; + else end_y = height; + shift = 8-(x&7); + if(x<0) + { + begin_x = -x>>3; + if(shift != 8) bool1 = 0; + } else begin_x = 0; + if(x+real_width > 128) end_x = 15-(x>>3), bool2 = 0; + else end_x = real_width-1>>3; + bool3 = (end_x == real_width-1>>3); + screen = ML_vram_adress()+(y+begin_y<<4)+(x>>3); + + for(i=begin_y ; i>3)+begin_x] << shift; + if(bool1) screen[begin_x] |= *p; + if(shift!=8) screen[begin_x+1] |= *(p+1); + for(j=begin_x+1 ; j>3)+j] << shift; + screen[j] |= *p; + if(shift!=8) screen[j+1] |= *(p+1); + } + } + line = bmp[i*(real_width>>3)+end_x]; + if(bool3) line &= -1<127 || y<1-height || y>63 || height<1 || width<1) return; + p = (char*)&line; + real_width = (width-1>>3<<3)+8; + if(y < 0) begin_y = -y; + else begin_y = 0; + if(y+height > 64) end_y = 64-y; + else end_y = height; + shift = 8-(x&7); + if(x<0) + { + begin_x = -x>>3; + if(shift != 8) bool1 = 0; + } else begin_x = 0; + if(x+real_width > 128) end_x = 15-(x>>3), bool2 = 0; + else end_x = real_width-1>>3; + bool3 = (end_x == real_width-1>>3); + screen = ML_vram_adress()+(y+begin_y<<4)+(x>>3); + + for(i=begin_y ; i>3)+begin_x]<>3)+j]<>3)+end_x]; + if(bool3) line &= -1<127 || y<1-height || y>63 || height<1 || width<1) return; + p = (char*)&line; + real_width = (width-1>>3<<3)+8; + if(y < 0) begin_y = -y; + else begin_y = 0; + if(y+height > 64) end_y = 64-y; + else end_y = height; + shift = 8-(x&7); + if(x<0) + { + begin_x = -x>>3; + if(shift != 8) bool1 = 0; + } else begin_x = 0; + if(x+real_width > 128) end_x = 15-(x>>3), bool2 = 0; + else end_x = real_width-1>>3; + bool3 = (end_x == real_width-1>>3); + screen = ML_vram_adress()+(y+begin_y<<4)+(x>>3); + + for(i=begin_y ; i>3)+begin_x] << shift; + if(bool1) screen[begin_x] ^= *p; + if(shift!=8) screen[begin_x+1] ^= *(p+1); + for(j=begin_x+1 ; j>3)+j] << shift; + screen[j] ^= *p; + if(shift!=8) screen[j+1] ^= *(p+1); + } + } + line = bmp[i*(real_width>>3)+end_x]; + if(bool3) line &= -1<120 || y<-7 || y>63) return; + if(y < 0) begin = -y; + if(y > 56) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i120 || y<-7 || y>63) return; + if(y < 0) begin = -y; + if(y > 56) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i120 || y<-7 || y>63) return; + if(y < 0) begin = -y; + if(y > 56) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i127 || y<-7 || y>63) return; + if(y < 0) begin = -y; + if(y > 56) end = 64-y; + shift = 8-(x&7); + if(x < 0) bool1 = 0; + if(x>120 || shift==8) bool2 = 0; + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i127 || y<-7 || y>63) return; + if(y < 0) begin = -y; + if(y > 56) end = 64-y; + shift = 8-(x&7); + if(x < 0) bool1 = 0; + if(x>120 || shift==8) bool2 = 0; + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i127 || y<-7 || y>63) return; + if(y < 0) begin = -y; + if(y > 56) end = 64-y; + shift = 8-(x&7); + if(x < 0) bool1 = 0; + if(x>120 || shift==8) bool2 = 0; + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i112 || y<-15 || y>63) return; + if(y < 0) begin = -y; + if(y > 48) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i112 || y<-15 || y>63) return; + if(y < 0) begin = -y; + if(y > 48) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i112 || y<-15 || y>63) return; + if(y < 0) begin = -y; + if(y > 48) end = 64-y; + shift = 8-(x&7); + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i127 || y<-15 || y>63) return; + if(y < 0) begin = -y; + if(y > 48) end = 64-y; + shift = 8-(x&7); + if(x < 0) bool1 = 0; + if(x<-8 || x>119) bool2 = 0; + if(x>111 || shift==8) bool3 = 0; + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i127 || y<-15 || y>63) return; + if(y < 0) begin = -y; + if(y > 48) end = 64-y; + shift = 8-(x&7); + if(x < 0) bool1 = 0; + if(x<-8 || x>119) bool2 = 0; + if(x>111 || shift==8) bool3 = 0; + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i127 || y<-15 || y>63) return; + if(y < 0) begin = -y; + if(y > 48) end = 64-y; + shift = 8-(x&7); + if(x < 0) bool1 = 0; + if(x<-8 || x>119) bool2 = 0; + if(x>111 || shift==8) bool3 = 0; + screen = ML_vram_adress()+(y+begin<<4)+(x>>3); + for(i=begin ; i