diff --git a/ZeDMD.h b/ZeDMD.h new file mode 100644 index 0000000..15bf3f2 --- /dev/null +++ b/ZeDMD.h @@ -0,0 +1,554 @@ +/** @file ZeDMD.h + * @brief ZeDMD client library. + * + * Connecting ZeDMD devices. + */ +#pragma once + +#define ZEDMD_VERSION_MAJOR 0 +#define ZEDMD_VERSION_MINOR 7 +#define ZEDMD_VERSION_PATCH 2 + +#define _ZEDMD_STR(x) #x +#define ZEDMD_STR(x) _ZEDMD_STR(x) + +#define ZEDMD_VERSION \ + ZEDMD_STR(ZEDMD_VERSION_MAJOR) \ + "." ZEDMD_STR(ZEDMD_VERSION_MINOR) "." ZEDMD_STR(ZEDMD_VERSION_PATCH) +#define ZEDMD_MINOR_VERSION ZEDMD_STR(ZEDMD_VERSION_MAJOR) "." ZEDMD_STR(ZEDMD_VERSION_MINOR) + +#define ZEDMD_MAX_WIDTH 256 +#define ZEDMD_MAX_HEIGHT 64 +#define ZEDMD_MAX_PALETTE 192 + +#ifdef _MSC_VER +#define ZEDMDAPI __declspec(dllexport) +#define ZEDMDCALLBACK __stdcall +#else +#define ZEDMDAPI __attribute__((visibility("default"))) +#define ZEDMDCALLBACK +#endif + +#include +#include + +#include + +typedef void(ZEDMDCALLBACK* ZeDMD_LogCallback)(const char* format, va_list args, const void* userData); + +class ZeDMDComm; +class ZeDMDWiFi; + +class ZEDMDAPI ZeDMD +{ + public: + ZeDMD(); + ~ZeDMD(); + + void SetLogCallback(ZeDMD_LogCallback callback, const void* userData); + + /** @brief Ignore a serial device when searching for ZeDMD + * + * While searching for a ZeDMD any serial ports are tested. + * This could cause trouble with other devices attached via + * USB or serial ports. + * Using this mfunction, a serial device could be excluded + * from the scan. + * This function could be called multiple times to ignore + * multiple devices. + * Another option to limit the searching is to use SetDevice(). + * @see SetDevice() + * @see Open() + * + * @param ignore_device the device to ignore + */ + void IgnoreDevice(const char* const ignore_device); + + /** @brief Use a specific serial device for ZeDMD + * + * Instead of serching through all serial devices for a ZeDMD, + * just use this device. + * @see Open() + * + * @param device the device + */ + void SetDevice(const char* const device); + + /** @brief Open the connection to ZeDMD + * + * Open a cennection to ZeDMD. Therefore all serial ports will be + * scanned. Use IgnoreDevice() to exclude one or more specific + * serial devices during that scan. Use SetDevice() to omit the + * the scan and to use a specific seriel device instead. + * @see IgnoreDevice() + * @see SetDevice() + */ + bool Open(); + + /** @brief Open the connection to ZeDMD + * + * Backward compatibiility version of Open() which additionally + * sets the frame size. Use Open() and SetFrameSize() instead. + * @see Open() + * @see SetFrameSize() + * + * @deprecated + * + * @param width the frame width + * @param height the frame height + */ + bool Open(uint16_t width, uint16_t height); + + /** @brief Open a WiFi connection to ZeDMD + * + * ZeDMD could be connected via WiFi instead of USB. + * The WiFi settings need to be stored in ZeDMD's EEPROM + * first using a USB connection. + * @see Open() + * @see SetWiFiSSID() + * @see SetWiFiPassword() + * @see SetWiFiPort() + * @see SaveSettings() + * + * @param ip the IPv4 address of the ZeDMD device + * @param port the port + */ + bool OpenWiFi(const char* ip, int port); + + /** @brief Close connection to ZeDMD + * + * Close connection to ZeDMD. + */ + void Close(); + + /** @brief Reset ZeDMD + * + * Reset ZeDMD. + */ + void Reset(); + + /** @brief Set the frame size + * + * Set the frame size of the content that will be displayed + * next on the ZeDMD device. Depending on the settings and + * the physical dimensions of the LED panels, the content + * will by centered and scaled correctly. + * @see EnablePreDownscaling() + * @see EnablePreUpscaling() + * @see EnableDownscaling() + * @see EnableUpscaling() + * + * @param width the frame width + * @param height the frame height + */ + void SetFrameSize(uint16_t width, uint16_t height); + + /** @brief Get the physical panel width + * + * Get the width of the physical dimensions of the LED panels. + * + * @return width + */ + uint16_t const GetWidth(); + + /** @brief Get the physical panel height + * + * Get the height of the physical dimensions of the LED panels. + * + * @return height + */ + uint16_t const GetHeight(); + + /** @brief Does ZeDMD run on an ESP32 S3? + * + * On an ESP32 S3 a native USB connection is used to increase + * the bandwidth. Furthermore double buffering is active. + * + * @return true if an ESP32 S3 is used. + */ + bool const IsS3(); + + /** @brief Set the palette + * + * Set the color palette to use to render gray scaled content. + * This library stores and tracks changes to 4, 16 and 64 + * color palettes individually. + * @see RenderGray2() + * @see RenderGray4() + * + * @param pPalette the palette as RGB array + * @param numColors 4, 16, or 64 colors palette + */ + void SetPalette(uint8_t* pPalette, uint8_t numColors); + + /** @brief Set the 4 color palette + * + * Backward compatibility version of SetPalette() to directly + * set the 4 color palette. + * @see RenderGray4() + * + * @param pPalette the palette as RGB array + * @param numColors 4, 16, or 64 colors palette + */ + void SetPalette(uint8_t* pPalette); + + /** @brief Set the default palette + * + * Use a default palette of shades of orange to render gray + * scaled content. + * @see RenderGray2() + * @see RenderGray4() + * + * @param bitDepth the bit depth, 2 means 4 colors, 4 means 16 colors + */ + void SetDefaultPalette(uint8_t bitDepth); + + /** @brief Get the default palette + * + * Get the values of a default palette of shades of orange. + * + * @param bitDepth the bit depth, 2 means 4 colors, 4 means 16 colors + * @return RGB array + */ + uint8_t* GetDefaultPalette(uint8_t bitDepth); + + /** @brief Test the panels attached to ZeDMD + * + * Renders a sequence of full red, full green and full blue frames. + */ + void LedTest(); + + /** @brief Enable debug mode + * + * ZeDMD will display various debug information as overlay to + * the displayed frame. + * @see https: + */ + void EnableDebug(); + + /** @brief Disable debug mode + * + * @see EnableDebug() + */ + void DisableDebug(); + + /** @brief Set the RGB order + * + * ZeDMD supports different LED panels. + * Depending on the panel, the RGB order needs to be adjusted. + * @see https: + * + * @param rgbOrder a value between 0 and 5 + */ + void SetRGBOrder(uint8_t rgbOrder); + + /** @brief Set the brightness + * + * Set the brightness of the LED panels. + * @see https: + * + * @param brightness a value between 0 and 15 + */ + void SetBrightness(uint8_t brightness); + + /** @brief Set the WiFi SSID + * + * Set the WiFi SSID ZeDMD should connect with. + * @see https: + * + * @param brightness a value between 0 and 15 + */ + void SetWiFiSSID(const char* const ssid); + + /** @brief Set the WiFi Password + * + * Set the WiFi Password ZeDMD should use to connect. + * @see https: + * + * @param password the password + */ + void SetWiFiPassword(const char* const password); + + /** @brief Set the WiFi Port + * + * Set the Port ZeDMD should listen at over WiFi. + * @see https: + * + * @param port the port + */ + void SetWiFiPort(int port); + + /** @brief Save the current setting + * + * Saves all current setting within ZeDMD's EEPROM to be used + * as defualt at its next start. + * @see https: + * @see SetRGBOrder() + * @see SetBrightness() + * @see SetWiFiSSID() + * @see SetWiFiPassword() + * @see SetWiFiPort() + * + * @param brightness a value between 0 and 15 + */ + void SaveSettings(); + + /** @brief Enable downscaling on the client side + * + * If enabled, the content will centered and scaled down to + * fit into the physical dimensions of the ZeDMD panels, + * before the content gets send to ZeDMD, if required. + */ + void EnablePreDownscaling(); + + /** @brief Disable downscaling on the client side + * + * @see EnablePreDownscaling() + */ + void DisablePreDownscaling(); + + /** @brief Enable upscaling on the client side + * + * If enabled, the content will centered and scaled up to + * fit into the physical dimensions of the ZeDMD panels, + * before the content gets send to ZeDMD, if required. + */ + void EnablePreUpscaling(); + + /** @brief Disable downscaling on the client side + * + * @see EnablePreUpscaling() + */ + void DisablePreUpscaling(); + + /** @brief Enable upscaling on ZeDMD itself + * + * If enabled and required, the content will centered and scaled + * up to fit into the physical dimensions of the ZeDMD panels + * by ZeDMD itself. Compared to EnablePreUpscaling(), less data + * needs to be send to ZeDMD and this might resut in a higher + * frame rate. + * But this optimized variant won't work with zone streaming modes. + * @see EnforceStreaming() + * @see DisableRGB24Streaming() + * @see RenderRgb24EncodedAs565() + */ + void EnableUpscaling(); + + /** @brief Disable upscaling on ZeDMD itself + * + * @see EnableUpscaling() + */ + void DisableUpscaling(); + + /** @brief Enforce zone streaming + * + * ZeDMD has two different render modes. One renders entire frames. + * This is the classic way and works pretty well. + * The other one is "zone streaming" which will be enable by this + * function. Zone streaming divides a frame into rectangular zones + * and only updates zones that have changes compared to the previous + * frame. This method results in less data that needs to be transfered + * and in smoother animations. But it takes a bit longer if the entire + * frame changes. Zone streaming is the default for RenderRGB24() and + * RenderRgb24EncodedAs565(). All other modes use the classic way by + * default unless EnforceStreaming() is called. + * @see RenderGray2() + * @see RenderGray4() + * @see RenderColoredGray6() + * @see RenderRgb24() + * @see DisableRGB24Streaming() + */ + void EnforceStreaming(); + + /** @brief Disable zone streaming for RGB24 + * + * By default, "zone streaming" is used for RenderRgb24(). That could be + * turned off using this function. + * @see EnforceStreaming() + */ + void DisableRGB24Streaming(); + + /** @brief Clear the screen + * + * Turn off all pixels of ZeDMD, so a blank black screen will be shown. + */ + void ClearScreen(); + + /** @brief Render a 2 bit gray scaled frame + * + * Renders a 2 bit gray scaled (indexed) frame using 4 colors. + * The colors will be used from the current palette set via SetPalette(). + * @see SetPalette() + * + * @param frame the indexed frame + */ + void RenderGray2(uint8_t* frame); + + /** @brief Render a 4 bit gray scaled frame + * + * Renders a 4 bit gray scaled (indexed) frame using 16 colors. + * The colors will be used from the current palette set via SetPalette(). + * @see SetPalette() + * + * @param frame the indexed frame + */ + void RenderGray4(uint8_t* frame); + + /** @brief Render a 6 bit frame + * + * Renders a 6 bit(indexed) frame using 64 colors. + * The colors will be used from the current palette set via SetPalette(). + * ZeDMD is able to rotate parts of the palette natively as defined by the + * Serum format until the next frame is received. + * @see SetPalette() + * + * @param frame the indexed frame + * @param rotations optional rotation command array according to the Serum + * format + */ + void RenderColoredGray6(uint8_t* frame, uint8_t* rotations); + + /** @brief Render a 6 bit frame + * + * Renders a 6 bit(indexed) frame using 64 colors. + * In oposite to standrad RenderColoredGray6(), the colors will not be used + * from the current palette set via SetPalette() but form the one provided + * to this function. + * ZeDMD is able to rotate parts of the palette natively as defined by the + * Serum format until the next frame is received. + * + * @param frame the indexed frame + * @param palette the colors to use + * @param rotations optional rotation command array according to the Serum + * format + */ + void RenderColoredGray6(uint8_t* frame, uint8_t* palette, uint8_t* rotations); + + /** @brief Render a RGB24 frame + * + * Renders a true color RGB frame. By default the zone streaming mode is + * used. The encoding is RGB888. + * @see DisableRGB24Streaming() + * + * @param frame the RGB frame + */ + void RenderRgb24(uint8_t* frame); + + /** @brief Render a RGB24 frame + * + * Renders a true color RGB frame. Only zone streaming mode is supported. + * The encoding is RGB565. + * + * @param frame the RGB frame + */ + void RenderRgb24EncodedAs565(uint8_t* frame); + + /** @brief Render a RGB24 frame + * + * Renders a true color RGB frame. Only zone streaming mode is supported. + * The encoding is RGB16. In fact, RGB16 is just another name for RGB565. + * + * @param frame the RGB frame + */ + void RenderRgb24EncodedAs16(uint8_t* frame) { RenderRgb24EncodedAs565(frame); } + + /** @brief Render a RGB565 frame + * + * Renders a true color RGB565 frame. Only zone streaming mode is supported. + * + * @param frame the RGB565 frame + */ + void RenderRgb565(uint16_t* frame); + + /** @brief Render a RGB16 frame + * + * Renders a true color RGB16 frame. Only zone streaming mode is supported. + * In fact, RGB16 is just another name for RGB565. + * + * @param frame the RGB16 frame + */ + void RenderRgb16(uint16_t* frame) { RenderRgb565(frame); } + + private: + bool UpdateFrameBuffer8(uint8_t* pFrame); + bool UpdateFrameBuffer24(uint8_t* pFrame); + bool UpdateFrameBuffer565(uint16_t* pFrame); + uint8_t GetScaleMode(uint16_t frameWidth, uint16_t frameHeight, uint16_t* pWidth, uint16_t* pHeight, + uint8_t* pXOffset, uint8_t* pYOffset); + int Scale(uint8_t* pScaledFrame, uint8_t* pFrame, uint8_t bytes, uint16_t* width, uint16_t* height); + int Scale16(uint8_t* pScaledFrame, uint16_t* pFrame, uint16_t* width, uint16_t* height, bool bigEndian); + + ZeDMDComm* m_pZeDMDComm; + ZeDMDWiFi* m_pZeDMDWiFi; + + uint16_t m_romWidth; + uint16_t m_romHeight; + + bool m_usb = false; + bool m_wifi = false; + bool m_hd = false; + bool m_downscaling = false; + bool m_upscaling = false; + bool m_streaming = false; + bool m_rgb24Streaming = true; + bool m_paletteChanged = false; + + uint8_t* m_pFrameBuffer; + uint8_t* m_pScaledFrameBuffer; + uint8_t* m_pCommandBuffer; + uint8_t* m_pPlanes; + uint8_t* m_pRgb565Buffer; + + uint8_t m_palette4[4 * 3] = {0}; + uint8_t m_palette16[16 * 3] = {0}; + uint8_t m_palette64[64 * 3] = {0}; + uint8_t m_DmdDefaultPalette2Bit[12] = {0, 0, 0, 144, 34, 0, 192, 76, 0, 255, 127, 0}; + uint8_t m_DmdDefaultPalette4Bit[48] = {0, 0, 0, 51, 25, 0, 64, 32, 0, 77, 38, 0, 89, 44, 0, 102, + 51, 0, 115, 57, 0, 128, 64, 0, 140, 70, 0, 153, 76, 0, 166, 83, + 0, 179, 89, 0, 191, 95, 0, 204, 102, 0, 230, 114, 0, 255, 127, 0}; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern ZEDMDAPI ZeDMD* ZeDMD_GetInstance(); + extern ZEDMDAPI void ZeDMD_IgnoreDevice(ZeDMD* pZeDMD, const char* const ignore_device); + extern ZEDMDAPI void ZeDMD_SetDevice(ZeDMD* pZeDMD, const char* const device); + extern ZEDMDAPI bool ZeDMD_Open(ZeDMD* pZeDMD); + extern ZEDMDAPI bool ZeDMD_OpenWiFi(ZeDMD* pZeDMD, const char* ip, int port); + extern ZEDMDAPI void ZeDMD_Close(ZeDMD* pZeDMD); + + extern ZEDMDAPI void ZeDMD_SetFrameSize(ZeDMD* pZeDMD, uint16_t width, uint16_t height); + extern ZEDMDAPI void ZeDMD_SetPalette(ZeDMD* pZeDMD, uint8_t* pPalette, uint8_t numColors); + extern ZEDMDAPI void ZeDMD_SetDefaultPalette(ZeDMD* pZeDMD, uint8_t bitDepth); + extern ZEDMDAPI uint8_t* ZeDMD_GetDefaultPalette(ZeDMD* pZeDMD, uint8_t bitDepth); + extern ZEDMDAPI void ZeDMD_LedTest(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_EnableDebug(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_DisableDebug(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_SetRGBOrder(ZeDMD* pZeDMD, uint8_t rgbOrder); + extern ZEDMDAPI void ZeDMD_SetBrightness(ZeDMD* pZeDMD, uint8_t brightness); + extern ZEDMDAPI void ZeDMD_SaveSettings(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_EnablePreDownscaling(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_DisablePreDownscaling(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_EnablePreUpscaling(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_DisablePreUpscaling(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_EnableUpscaling(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_DisableUpscaling(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_SetWiFiSSID(ZeDMD* pZeDMD, const char* const ssid); + extern ZEDMDAPI void ZeDMD_SetWiFiPassword(ZeDMD* pZeDMD, const char* const password); + extern ZEDMDAPI void ZeDMD_SetWiFiPort(ZeDMD* pZeDMD, int port); + extern ZEDMDAPI void ZeDMD_EnforceStreaming(ZeDMD* pZeDMD); + + extern ZEDMDAPI void ZeDMD_ClearScreen(ZeDMD* pZeDMD); + extern ZEDMDAPI void ZeDMD_RenderGray2(ZeDMD* pZeDMD, uint8_t* frame); + extern ZEDMDAPI void ZeDMD_RenderGray4(ZeDMD* pZeDMD, uint8_t* frame); + extern ZEDMDAPI void ZeDMD_RenderColoredGray6(ZeDMD* pZeDMD, uint8_t* frame, uint8_t* rotations); + extern ZEDMDAPI void ZeDMD_RenderRgb24(ZeDMD* pZeDMD, uint8_t* frame); + extern ZEDMDAPI void ZeDMD_RenderRgb24EncodedAs565(ZeDMD* pZeDMD, uint8_t* frame); + +#ifdef __cplusplus +} +#endif diff --git a/paste.png b/paste.png index 779049c..6429885 100644 Binary files a/paste.png and b/paste.png differ diff --git a/resource.h b/resource.h index 3f091cf..81afec2 100644 --- a/resource.h +++ b/resource.h @@ -1,7 +1,7 @@ -//{{NO_DEPENDENCIES}} -// fichier Include Microsoft Visual C++. -// Utilisé par ColorizingDMD.rc -// + + + + #define IDC_MYICON 2 #define IDD_COLORIZINGDMD_DIALOG 102 #define IDI_CROM 103 @@ -429,8 +429,8 @@ #define IDC_FRAME64 1217 #define IDC_STATIC -1 -// Next default values for new objects -// + + #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 diff --git a/serum.h b/serum.h index 5e5dddd..6eb43b2 100644 --- a/serum.h +++ b/serum.h @@ -2,65 +2,88 @@ typedef unsigned char UINT8; typedef unsigned short UINT16; -typedef unsigned int UINT32; typedef unsigned int UINT; -typedef struct +enum { - // in former format (prior to 2.0.0) the returned frame replaces the original frame, so this is not - // part of this - UINT8* frame; // return the colorized frame - UINT8* palette; // and its palette - UINT8* rotations; // and its color rotations - UINT32* triggerID; // return 0xffff if no trigger for that frame, the ID of the trigger if one is set for that frame - UINT* frameID; // for CDMD ingame tester -}Serum_Frame; + SERUM_V1, + SERUM_V2 +}; + + +enum +{ + FLAG_REQUEST_32P_FRAMES = 1, + FLAG_REQUEST_64P_FRAMES = 2, + FLAG_REQUEST_FILL_MODIFIED_ELEMENTS = 4, +}; + +enum +{ + FLAG_RETURNED_32P_FRAME_OK = 1, + FLAG_RETURNED_64P_FRAME_OK = 2, +}; + +enum +{ + FLAG_RETURNED_V1_ROTATED = 0x10000, + FLAG_RETURNED_V2_ROTATED32 = 0x10000, + FLAG_RETURNED_V2_ROTATED64 = 0x20000, +}; typedef struct { - // the frame (frame32 or frame64) corresponding to the original resolution must ALWAYS be defined - // but the frame corresponding to the extra resolution must be defined only if we request it - // if a frame is defined, its width, rotations and rotationsinframe must be defined + + UINT8* frame; + UINT8* palette; + UINT8* rotations; + + + UINT16* frame32; - UINT* width32; // 0 is returned if the 32p colorized frame is not available for this frame + UINT width32; UINT16* rotations32; - UINT16* rotationsinframe32; // [(96 or 128)*32*2] precalculated array to tell if a color is in a color rotations of the frame ([X*Y*0]=0xffff if not part of a rotation) + UINT16* rotationsinframe32; + UINT8* modifiedelements32; UINT16* frame64; - UINT* width64; // 0 is returned if the 64p colorized frame is not available for this frame + UINT width64; UINT16* rotations64; - UINT16* rotationsinframe64; // [(192 or 256)*64*2] precalculated array to tell if a color is in a color rotations of the frame ([X*Y*0]=0xffff if not part of a rotation) - UINT32* triggerID; // return 0xffff if no trigger for that frame, the ID of the trigger if one is set for that frame - UINT8* flags; // return flags: - // if flags & 1 : frame32 has been filled - // if flags & 2 : frame64 has been filled - // if none of them, display the original frame - UINT* frameID; // for CDMD ingame tester -}Serum_Frame_New; - -const int MAX_DYNA_4COLS_PER_FRAME = 16; // max number of color sets for dynamic content for each frame (old version) -const int MAX_DYNA_SETS_PER_FRAMEN = 32; // max number of color sets for dynamic content for each frame (new version) -const int MAX_SPRITE_SIZE = 128; // maximum size of the sprites -const int MAX_SPRITE_WIDTH = 256; // maximum width of the new sprites -const int MAX_SPRITE_HEIGHT = 64; // maximum height of the new sprites -const int MAX_SPRITES_PER_FRAME = 32; // maximum amount of sprites to look for per frame -const int MAX_COLOR_ROTATIONS = 8; // maximum amount of color rotations per frame -const int MAX_COLOR_ROTATIONN = 4; // maximum number of new color rotations per frame -const int MAX_LENGTH_COLOR_ROTATION = 64; // maximum number of new colors in a rotation -const int MAX_SPRITE_DETECT_AREAS = 4; // maximum number of areas to detect the sprite + UINT16* rotationsinframe64; + UINT8* modifiedelements64; + + UINT SerumVersion; + + + + + + + + UINT8 flags; + unsigned int nocolors; + unsigned int ntriggers; + UINT triggerID; + UINT frameID; + UINT rotationtimer; +}Serum_Frame_Struc; -const int PALETTE_SIZE = 64 * 3; // size of a palette -const int ROTATION_SIZE = 3 * MAX_COLOR_ROTATIONS; // size of a color rotation block -const int MAX_SPRITE_TO_DETECT = 16; // max number of sprites detected in a frame -const int MAX_BACKGROUND_IMAGES = 255; // max number of background images +const int MAX_DYNA_4COLS_PER_FRAME = 16; +const int MAX_DYNA_SETS_PER_FRAMEN = 32; +const int MAX_SPRITE_SIZE = 128; +const int MAX_SPRITE_WIDTH = 256; +const int MAX_SPRITE_HEIGHT = 64; +const int MAX_SPRITES_PER_FRAME = 32; +const int MAX_COLOR_ROTATIONS = 8; +const int MAX_COLOR_ROTATIONN = 4; +const int MAX_LENGTH_COLOR_ROTATION = 64; +const int MAX_SPRITE_DETECT_AREAS = 4; -// Flags sent with Serum_Load -const int FLAG_REQUEST_32P_FRAMES = 1; // there is a output DMD which is 32 leds high -const int FLAG_REQUEST_64P_FRAMES = 2; // there is a output h is 64 leds high -const int FLAG_32P_FRAME_OK = 1; // the 32p frame has been filled -const int FLAG_64P_FRAME_OK = 2; // the 64p frame has been filled +const int PALETTE_SIZE = 64 * 3; +const int ROTATION_SIZE = 3 * MAX_COLOR_ROTATIONS; +const int MAX_BACKGROUND_IMAGES = 255; -typedef bool (*Serum_LoadFunc)(const char* const altcolorpath, const char* const romname, unsigned int* pnocolors, unsigned int* pntriggers, unsigned char flags, unsigned int* width32, unsigned int* width64, UINT8* isnewformat); +typedef Serum_Frame_Struc* (*Serum_LoadFunc)(const char* const altcolorpath, const char* const romname, UINT8 flags); typedef void (*Serum_DisposeFunc)(void); -typedef bool (*Serum_ColorizeFunc)(UINT8* frame, Serum_Frame* poldframe, Serum_Frame_New* pnewframe); -typedef bool (*Serum_ApplyRotationsFunc)(Serum_Frame* poldframe); -typedef bool (*Serum_ApplyRotationsNFunc)(Serum_Frame_New* pnewframe, UINT8* modelements32, UINT8* modelements64); +typedef UINT (*Serum_ColorizeFunc)(UINT8* frame); +typedef UINT (*Serum_RotateFunc)(void); +typedef const char* (*Serum_GetVersionFunc)(void); diff --git a/serum64.dll b/serum64.dll index 2bccb57..8bf4f2d 100644 Binary files a/serum64.dll and b/serum64.dll differ diff --git a/serumdll.cpp b/serumdll.cpp index f77bedc..d153164 100644 --- a/serumdll.cpp +++ b/serumdll.cpp @@ -1,194 +1,39 @@ #include "serumdll.h" -HINSTANCE hSerumDLL; -Serum_LoadFunc serum_Load; -Serum_DisposeFunc serum_Dispose; -Serum_ColorizeFunc serum_Colorize; -Serum_ApplyRotationsFunc serum_ApplyRotations; -Serum_ApplyRotationsNFunc serum_ApplyRotationsN; -UINT8 isNewFormat = 0; // is the file a new Serum 2+ version (1) or a former version one (0)? -Serum_Frame MyOldFrame; // structure to communicate with former format Serum -Serum_Frame_New MyNewFrame; // structure to communicate with new format Serum -UINT8* ModifiedElements32 = NULL; // for the new color rotations in 32P, optional -UINT8* ModifiedElements64 = NULL; // for the new color rotations in 64P, optional -UINT triggerID; // return PuP pack trigger ID (0xffff if no trigger) -UINT8 returnflag; // what frame resolutions are returned with new format colorization -UINT32 noColors; // number of colors of the original ROM (4 or 16) -UINT32 fWidth, fHeight; // dimensions of the original ROM (MUST BE KNOWN BEFORE calling Serum functions) -UINT width32 = 0, width64 = 0; // widths of the colorized frames returned respectively for the height=32 and height=64 frames -UINT ntriggers = 0; // number of PuP triggers found in the file +UINT8* ModifiedElements32 = NULL; +UINT8* ModifiedElements64 = NULL; -bool Load_Serum_DLL(void) -{ - // Function to load the serum library and all its needed functions, call it in your initial code - // replace File_SerumDLL by a const char* with the full path and name of the DLL - // like "c:\\visual pinball\\vpinmame\\serum64.dll" - char tbuf[MAX_PATH]; - GetModuleFileNameA(NULL, tbuf, MAX_PATH); - //strcpy_s(tbuf, MAX_PATH, vpdir); - //if (tbuf[strlen(tbuf) - 1] != '\\' && tbuf[strlen(tbuf) - 1] != '/') strcat_s(tbuf, MAX_PATH, "\\"); - int ti = (int)strlen(tbuf); - while (tbuf[ti] != '\\' && tbuf[ti] != '/' && ti > 0) ti--; - if (ti == 0) - { - cprintf(true, "Error in getting the module path for serum64.dll", tbuf); - return false; - } - tbuf[ti + 1] = 0; - strcat_s(tbuf, MAX_PATH, "serum64.dll"); - hSerumDLL = LoadLibraryA(tbuf); - if (hSerumDLL == NULL) - { - cprintf(true, "Can't open %s", tbuf); - return false; - } - serum_Load = (Serum_LoadFunc)GetProcAddress(hSerumDLL, "Serum_Load"); - if (serum_Load == NULL) - { - cprintf(true, "Can't find Serum_Load function in the DLL"); - FreeLibrary(hSerumDLL); - return false; - } - serum_Dispose = (Serum_DisposeFunc)GetProcAddress(hSerumDLL, "Serum_Dispose"); - if (serum_Dispose == NULL) - { - cprintf(true, "Can't find Serum_Dispose function in the DLL"); - FreeLibrary(hSerumDLL); - return false; - } - serum_Colorize = (Serum_ColorizeFunc)GetProcAddress(hSerumDLL, "Serum_Colorize"); - if (serum_Colorize == NULL) - { - cprintf(true, "Can't find Serum_Colorize function in the DLL"); - FreeLibrary(hSerumDLL); - return false; - } - serum_ApplyRotations = (Serum_ApplyRotationsFunc)GetProcAddress(hSerumDLL, "Serum_ApplyRotations"); - if (serum_ApplyRotations == NULL) - { - cprintf(true, "Can't find Serum_ApplyRotations function in the DLL"); - FreeLibrary(hSerumDLL); - return false; - } - serum_ApplyRotationsN = (Serum_ApplyRotationsNFunc)GetProcAddress(hSerumDLL, "Serum_ApplyRotationsN"); - if (serum_ApplyRotationsN == NULL) - { - cprintf(true, "Can't find Serum_ApplyRotationsN function in the DLL"); - FreeLibrary(hSerumDLL); - return false; - } - return true; -} -void Free_element(void* pElement) -{ - if (pElement) - { - free(pElement); - pElement = NULL; - } -} -void Free_Serum(void) -{ - Free_element(MyOldFrame.frame); - Free_element(MyOldFrame.palette); - Free_element(MyOldFrame.rotations); - Free_element(MyNewFrame.frame32); - Free_element(MyNewFrame.frame64); - Free_element(MyNewFrame.rotations32); - Free_element(MyNewFrame.rotations64); - Free_element(MyNewFrame.rotationsinframe32); - Free_element(MyNewFrame.rotationsinframe64); - Free_element(ModifiedElements32); - Free_element(ModifiedElements64); -} +UINT32 fWidth, fHeight; + + +Serum_Frame_Struc* pSerum; bool Allocate_Serum(void) { - MyOldFrame.palette = NULL; - MyOldFrame.rotations = NULL; - MyNewFrame.frame32 = NULL; - MyNewFrame.frame64 = NULL; - MyNewFrame.rotations32 = NULL; - MyNewFrame.rotations64 = NULL; - MyNewFrame.rotationsinframe32 = NULL; - MyNewFrame.rotationsinframe64 = NULL; - if (isNewFormat == 0) + + + ModifiedElements32 = (UINT8*)malloc(pSerum->width32 * 32); + ModifiedElements64 = (UINT8*)malloc(pSerum->width64 * 64); + if (!ModifiedElements32 || !ModifiedElements64) { - MyOldFrame.frame = (UINT8*)malloc(fWidth * fHeight); - MyOldFrame.palette = (UINT8*)malloc(3 * 64); - MyOldFrame.rotations = (UINT8*)malloc(3 * MAX_COLOR_ROTATIONS); - MyOldFrame.triggerID = &triggerID; - if (!MyOldFrame.frame || !MyOldFrame.palette || !MyOldFrame.rotations) - { - cprintf(false, "Can't get memory for the DLL with old format"); - FreeLibrary(hSerumDLL); - Free_Serum(); - serum_Dispose(); - return false; - } - } - else - { - MyNewFrame.flags = &returnflag; - MyNewFrame.triggerID = &triggerID; - // ---------- Both ModifiedElementsXX are optional so this code may be skipped ---------- - // They are only needed if you only want to change the modified pixels of a frame after a color rotation - ModifiedElements32 = (UINT8*)malloc(width32 * 32); - ModifiedElements64 = (UINT8*)malloc(width64 * 64); - if (!ModifiedElements32 || !ModifiedElements64) - { - cprintf(false, "Can't get memory for the modified pixels in the DLL"); - FreeLibrary(hSerumDLL); - Free_Serum(); - serum_Dispose(); - return false; - } - // -------------------------------------------------------------------------------------- - if (width32 > 0) - { - MyNewFrame.frame32 = (UINT16*)malloc(2 * 32 * width32); - MyNewFrame.rotations32 = (UINT16*)malloc(2 * MAX_COLOR_ROTATIONN * MAX_LENGTH_COLOR_ROTATION); - MyNewFrame.rotationsinframe32 = (UINT16*)malloc(2 * 2 * 32 * width32); - MyNewFrame.width32 = &width32; - if (!MyNewFrame.frame32 || !MyNewFrame.rotations32 || !MyNewFrame.rotationsinframe32) - { - cprintf(false, "Can't get memory for the 32P elements of the DLL with new format"); - FreeLibrary(hSerumDLL); - Free_Serum(); - serum_Dispose(); - return false; - } - } - if (width64 > 0) - { - MyNewFrame.frame64 = (UINT16*)malloc(2 * 64 * width64); - MyNewFrame.rotations64 = (UINT16*)malloc(2 * MAX_COLOR_ROTATIONN * MAX_LENGTH_COLOR_ROTATION); - MyNewFrame.rotationsinframe64 = (UINT16*)malloc(2 * 2 * 64 * width64); - MyNewFrame.width64 = &width64; - if (!MyNewFrame.frame64 || !MyNewFrame.rotations64 || !MyNewFrame.rotationsinframe64) - { - cprintf(false, "Can't get memory for the 32P elements of the DLL with new format"); - FreeLibrary(hSerumDLL); - Free_Serum(); - serum_Dispose(); - return false; - } - } + cprintf(false, "Can't get memory for the modified pixels in the DLL"); + Serum_Dispose(); + return false; } return true; } bool InitLibSerum(char* vpdir, char* fname) { - if (!Load_Serum_DLL()) return false; char tbuf[MAX_PATH]; strcpy_s(tbuf, MAX_PATH, vpdir); if (tbuf[strlen(tbuf) - 1] != '\\' && tbuf[strlen(tbuf) - 1] != '/') strcat_s(tbuf, MAX_PATH, "\\"); strcat_s(tbuf, MAX_PATH, "altcolor\\"); - if (!serum_Load(tbuf, fname, &noColors, &ntriggers, FLAG_REQUEST_32P_FRAMES | FLAG_REQUEST_64P_FRAMES, &width32, &width64, &isNewFormat)) + pSerum = Serum_Load(tbuf, fname, FLAG_REQUEST_32P_FRAMES | FLAG_REQUEST_64P_FRAMES); + if (!pSerum) { cprintf(false, "Serum DLL can't load the file, check the directory"); FreeLibrary(hSerumDLL); @@ -197,75 +42,67 @@ bool InitLibSerum(char* vpdir, char* fname) if (!Allocate_Serum()) return false; return true; } -/// - -/// -/// colorize a frame (in return, buffers are available if non NULL) -/// -/// inbound frame -/// colorized frame if old format -/// colorized frame in 32P if new format -/// colorized frame in 32P if new format -/// return true if there is an active color rotation, false if not -bool ColorizeAFrame(UINT8* vpframe, UINT8** oldframe, UINT16** newframe32, UINT16** newframe64, UINT* frameID, bool* is32fr, bool* is64fr) +void UpscaleRGB565Frame(UINT16* frame32, UINT16* frame64, UINT width32) { - if (isNewFormat == 0) + for (UINT tj = 0; tj < 32; tj++) { - MyOldFrame.frameID = frameID; - serum_Colorize(vpframe, &MyOldFrame, NULL); - *oldframe = MyOldFrame.frame; - *newframe32 = NULL; - *newframe64 = NULL; - for (int ti = 0; ti < MAX_COLOR_ROTATIONS; ti++) + for (UINT ti = 0; ti < width32; ti++) { - if (MyOldFrame.rotations[3 * ti] != 255) return true; + UINT16 val = frame32[ti + tj * width32]; + frame64[ti * 2 + (tj * 2) * width32 * 2] = frame64[ti * 2 + 1 + (tj * 2) * width32 * 2] = + frame64[ti * 2 + (tj * 2 + 1) * width32 * 2] = frame64[ti * 2 + 1 + (tj * 2 + 1) * width32 * 2] = val; } } - else +} + +void DownscaleRGB565Frame(UINT16* frame64, UINT16* frame32, UINT width64) +{ + for (UINT tj = 0; tj < 32; tj++) { - MyNewFrame.frameID = frameID; - serum_Colorize(vpframe, NULL, &MyNewFrame); - *newframe32 = MyNewFrame.frame32; - *newframe64 = MyNewFrame.frame64; - *oldframe = NULL; - if (*MyNewFrame.width32 > 0) *is32fr = true; else *is32fr = false; - if (*MyNewFrame.width64 > 0) *is64fr = true; else *is64fr = false; - for (int ti = 0; ti < MAX_COLOR_ROTATIONN; ti++) + for (UINT ti = 0; ti < width64 / 2; ti++) { - if (MyNewFrame.rotations32[MAX_LENGTH_COLOR_ROTATION * ti] != 0) return true; - if (MyNewFrame.rotations64[MAX_LENGTH_COLOR_ROTATION * ti] != 0) return true; + frame32[ti + tj * width64 / 2] = frame64[ti * 2 + (tj * 2) * width64]; } } +} + + + + + + + + + +bool ColorizeAFrame(UINT8* vpframe, UINT16** newframe32, UINT16** newframe64, UINT* frameID, bool* is32fr, bool* is64fr) +{ + Serum_Colorize(vpframe); + *newframe32 = pSerum->frame32; + *newframe64 = pSerum->frame64; + if (pSerum->width32 > 0) *is32fr = true; else *is32fr = false; + if (pSerum->width64 > 0) *is64fr = true; else *is64fr = false; + for (int ti = 0; ti < MAX_COLOR_ROTATIONN; ti++) + { + if (pSerum->rotations32[MAX_LENGTH_COLOR_ROTATION * ti] != 0) return true; + if (pSerum->rotations64[MAX_LENGTH_COLOR_ROTATION * ti] != 0) return true; + } return false; } -bool ColorRotateAFrame(UINT8** oldframe, UINT16** newframe32, UINT8** modelt32, UINT16** newframe64, UINT8** modelt64) +bool ColorRotateAFrame(UINT16** newframe32, UINT8** modelt32, UINT16** newframe64, UINT8** modelt64) { bool isrot; - if (isNewFormat) - { - isrot = serum_ApplyRotationsN(&MyNewFrame, ModifiedElements32, ModifiedElements64); // if you don't need them replace ModifiedElementsXX by NULL - *newframe32 = MyNewFrame.frame32; - *newframe64 = MyNewFrame.frame64; - if (modelt32) *modelt32 = ModifiedElements32; - if (modelt64) *modelt64 = ModifiedElements64; - *oldframe = NULL; - } - else - { - isrot = serum_ApplyRotations(&MyOldFrame); - *oldframe = MyOldFrame.frame; - *newframe32 = NULL; - *newframe64 = NULL; - } + isrot = Serum_Rotate(); + *newframe32 = pSerum->frame32; + *newframe64 = pSerum->frame64; + if (modelt32) *modelt32 = ModifiedElements32; + if (modelt64) *modelt64 = ModifiedElements64; return isrot; } void StopLibSerum(void) { - Free_Serum(); - serum_Dispose(); - FreeLibrary(hSerumDLL); + Serum_Dispose(); } diff --git a/serumdll.h b/serumdll.h index 6972616..154f938 100644 --- a/serumdll.h +++ b/serumdll.h @@ -1,10 +1,12 @@ #pragma once -#include "serum.h" +#include "serumwin.h" #include void cprintf(bool isFlash, const char* format, ...); bool InitLibSerum(char* vpdir, char* fname); void StopLibSerum(void); -bool ColorizeAFrame(UINT8* vpframe, UINT8** oldframe, UINT16** newframe32, UINT16** newframe64, UINT* frameID, bool* is32fr, bool* is64fr); -bool ColorRotateAFrame(UINT8** oldframe, UINT16** newframe32, UINT8** modelt32, UINT16** newframe64, UINT8** modelt64); +bool ColorizeAFrame(UINT8* vpframe, UINT16** newframe32, UINT16** newframe64, UINT* frameID, bool* is32fr, bool* is64fr); +bool ColorRotateAFrame(UINT16** newframe32, UINT8** modelt32, UINT16** newframe64, UINT8** modelt64); +void UpscaleRGB565Frame(UINT16* frame32, UINT16* frame64, UINT width32); +void DownscaleRGB565Frame(UINT16* frame64, UINT16* frame32, UINT width64); diff --git a/serumwin.cpp b/serumwin.cpp new file mode 100644 index 0000000..a3ffad3 --- /dev/null +++ b/serumwin.cpp @@ -0,0 +1,83 @@ +#include "serumwin.h" + +HINSTANCE hSerumDLL; +Serum_LoadFunc Serum_Load; +Serum_DisposeFunc Serum_Dispose, Serum_DisableColorization, Serum_EnableColorization; +Serum_ColorizeFunc Serum_Colorize; +Serum_RotateFunc Serum_Rotate; +Serum_GetVersionFunc Serum_GetVersion, Serum_GetMinorVersion; + +void Serum_ReleaseDLL(void) +{ + FreeLibrary(hSerumDLL); +} + +bool Serum_LoadDLL(const char* File_SerumDLL) +{ + + + + hSerumDLL = LoadLibraryA(File_SerumDLL); + if (hSerumDLL == NULL) + { + + return false; + } + Serum_Load = (Serum_LoadFunc)GetProcAddress(hSerumDLL, "Serum_Load"); + if (Serum_Load == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_Dispose = (Serum_DisposeFunc)GetProcAddress(hSerumDLL, "Serum_Dispose"); + if (Serum_Dispose == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_Colorize = (Serum_ColorizeFunc)GetProcAddress(hSerumDLL, "Serum_Colorize"); + if (Serum_Colorize == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_Rotate = (Serum_RotateFunc)GetProcAddress(hSerumDLL, "Serum_Rotate"); + if (Serum_Rotate == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_GetVersion = (Serum_GetVersionFunc)GetProcAddress(hSerumDLL, "Serum_GetVersion"); + if (Serum_GetVersion == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_GetMinorVersion = (Serum_GetVersionFunc)GetProcAddress(hSerumDLL, "Serum_GetMinorVersion"); + if (Serum_GetMinorVersion == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_DisableColorization = (Serum_DisposeFunc)GetProcAddress(hSerumDLL, "Serum_DisableColorization"); + if (Serum_DisableColorization == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + Serum_EnableColorization = (Serum_DisposeFunc)GetProcAddress(hSerumDLL, "Serum_EnableColorization"); + if (Serum_DisableColorization == NULL) + { + + Serum_ReleaseDLL(); + return false; + } + return true; +} diff --git a/serumwin.h b/serumwin.h new file mode 100644 index 0000000..faa7ade --- /dev/null +++ b/serumwin.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "serum.h" + +extern HINSTANCE hSerumDLL; +extern Serum_LoadFunc Serum_Load; +extern Serum_DisposeFunc Serum_Dispose, Serum_DisableColorization, Serum_EnableColorization; +extern Serum_ColorizeFunc Serum_Colorize; +extern Serum_RotateFunc Serum_Rotate; +extern Serum_GetVersionFunc Serum_GetVersion, Serum_GetMinorVersion; + +void Serum_ReleaseDLL(void); + +bool Serum_LoadDLL(const char* File_SerumDLL); diff --git a/targetver.h b/targetver.h index 7586c32..f1ba6db 100644 --- a/targetver.h +++ b/targetver.h @@ -1,6 +1,6 @@ #pragma once -// // L'inclusion de SDKDDKVer.h définit la version de plateforme Windows la plus haute disponible. -// Si vous voulez générer votre application pour une plateforme Windows précédente, incluez WinSDKVer.h et -// définissez la macro _WIN32_WINNT sur la plateforme à prendre en charge avant d'inclure SDKDDKVer.h. + + + #include diff --git a/zedmd64.dll b/zedmd64.dll new file mode 100644 index 0000000..81f2c0d Binary files /dev/null and b/zedmd64.dll differ diff --git a/zedmd64.lib b/zedmd64.lib new file mode 100644 index 0000000..83bf000 Binary files /dev/null and b/zedmd64.lib differ diff --git a/zedmd_static.lib b/zedmd_static.lib new file mode 100644 index 0000000..4aa7f0f Binary files /dev/null and b/zedmd_static.lib differ