diff --git a/Client/core/Graphics/CRenderItem.Gif.cpp b/Client/core/Graphics/CRenderItem.Gif.cpp new file mode 100644 index 0000000000..c9db491f34 --- /dev/null +++ b/Client/core/Graphics/CRenderItem.Gif.cpp @@ -0,0 +1,140 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" + +//////////////////////////////////////////////////////////////// +// +// CGifItem::PostConstruct +// +// +// +//////////////////////////////////////////////////////////////// +void CGifItem::PostConstruct(CRenderItemManager* pManager, uint width, uint height) +{ + Super::PostConstruct(pManager); + + m_uiSizeX = width; + m_uiSizeY = height; + m_uiSurfaceSizeX = width; + m_uiSurfaceSizeY = height; + + CreateUnderlyingData(); +} + +//////////////////////////////////////////////////////////////// +// +// CGifItem::PreDestruct +// +// +// +//////////////////////////////////////////////////////////////// +void CGifItem::PreDestruct() +{ + ReleaseUnderlyingData(); + Super::PreDestruct(); +} + +//////////////////////////////////////////////////////////////// +// +// CGifItem::IsValid +// +// Check underlying data is present +// +//////////////////////////////////////////////////////////////// +bool CGifItem::IsValid() +{ + return m_pD3DTexture != nullptr; +} + +//////////////////////////////////////////////////////////////// +// +// CGifItem::OnLostDevice +// +// Release device stuff +// +//////////////////////////////////////////////////////////////// +void CGifItem::OnLostDevice() +{ + // Nothing required for CGifItem +} + +//////////////////////////////////////////////////////////////// +// +// CGifItem::OnResetDevice +// +// Recreate device stuff +// +//////////////////////////////////////////////////////////////// +void CGifItem::OnResetDevice() +{ + // Nothing required for CGifItem +} + +//////////////////////////////////////////////////////////////// +// +// CGifItem::CreateUnderlyingData +// +// From file +// +//////////////////////////////////////////////////////////////// +void CGifItem::CreateUnderlyingData() +{ + assert(!m_pD3DRenderTargetSurface); + assert(!m_pD3DTexture); + + D3DXCreateTexture(m_pDevice, m_uiSizeX, m_uiSizeY, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, (IDirect3DTexture9**)&m_pD3DTexture); + + // Check texture created + if (!m_pD3DTexture) + return; + + // D3DXCreateTexture sets width and height to 1 if the argument value was 0 + // See: https://docs.microsoft.com/en-us/windows/desktop/direct3d9/d3dxcreatetexture + if (m_uiSizeX == 0) + m_uiSizeX = 1; + + if (m_uiSizeY == 0) + m_uiSizeY = 1; + + // Get the render target surface here for convenience + ((IDirect3DTexture9*)m_pD3DTexture)->GetSurfaceLevel(0, &m_pD3DRenderTargetSurface); + + // Update surface size, although it probably will be unchanged | Todo: Remove this + D3DSURFACE_DESC desc; + m_pD3DRenderTargetSurface->GetDesc(&desc); + m_uiSurfaceSizeX = desc.Width; + m_uiSurfaceSizeY = desc.Height; + + m_iMemoryKBUsed = CRenderItemManager::CalcD3DResourceMemoryKBUsage(m_pD3DRenderTargetSurface); +} + +//////////////////////////////////////////////////////////////// +// +// CGifItem::ReleaseUnderlyingData +// +// +// +//////////////////////////////////////////////////////////////// +void CGifItem::ReleaseUnderlyingData() +{ + SAFE_RELEASE(m_pD3DRenderTargetSurface); + SAFE_RELEASE(m_pD3DTexture); +} + +void CGifItem::Resize(const CVector2D& size) +{ + // Update size + m_uiSizeX = static_cast(size.fX); + m_uiSizeY = static_cast(size.fY); + + // Recreate texture + ReleaseUnderlyingData(); + CreateUnderlyingData(); +} diff --git a/Client/core/Graphics/CRenderItemManager.cpp b/Client/core/Graphics/CRenderItemManager.cpp index 3351350ba9..0f4eb0c1c9 100644 --- a/Client/core/Graphics/CRenderItemManager.cpp +++ b/Client/core/Graphics/CRenderItemManager.cpp @@ -171,6 +171,32 @@ CVectorGraphicItem* CRenderItemManager::CreateVectorGraphic(uint width, uint hei return pVectorItem; } +//////////////////////////////////////////////////////////////// +// +// CRenderItemManager::CreateGif +// +// +// +//////////////////////////////////////////////////////////////// +CGifItem* CRenderItemManager::CreateGif(uint width, uint height) +{ + if (!CanCreateRenderItem(CGifItem::GetClassId())) + return nullptr; + + CGifItem* pGifItem = new CGifItem; + pGifItem->PostConstruct(this, width, height); + + if (!pGifItem->IsValid()) + { + SAFE_RELEASE(pGifItem); + return nullptr; + } + + UpdateMemoryUsage(); + + return pGifItem; +} + //////////////////////////////////////////////////////////////// // // CRenderItemManager::CreateRenderTarget diff --git a/Client/core/Graphics/CRenderItemManager.h b/Client/core/Graphics/CRenderItemManager.h index 8aed064393..7533f0900d 100644 --- a/Client/core/Graphics/CRenderItemManager.h +++ b/Client/core/Graphics/CRenderItemManager.h @@ -34,6 +34,7 @@ class CRenderItemManager : public CRenderItemManagerInterface bool bForce = false); virtual CScreenSourceItem* CreateScreenSource(uint uiSizeX, uint uiSizeY); virtual CVectorGraphicItem* CreateVectorGraphic(uint width, uint height); + virtual CGifItem* CreateGif(uint width,uint height); virtual CWebBrowserItem* CreateWebBrowser(uint uiSizeX, uint uiSizeY); virtual bool SetRenderTarget(CRenderTargetItem* pItem, bool bClear); virtual void EnableSetRenderTargetOldVer(bool bEnable); diff --git a/Client/mods/deathmatch/logic/CClientDisplay.h b/Client/mods/deathmatch/logic/CClientDisplay.h index 291180c3c5..ede6d8783c 100644 --- a/Client/mods/deathmatch/logic/CClientDisplay.h +++ b/Client/mods/deathmatch/logic/CClientDisplay.h @@ -18,6 +18,7 @@ enum eDisplayType { DISPLAY_TEXT, DISPLAY_VECTORGRAPHIC, + DISPLAY_GIF }; class CClientDisplay diff --git a/Client/mods/deathmatch/logic/CClientEntity.h b/Client/mods/deathmatch/logic/CClientEntity.h index 031c1418b8..4c27daa918 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.h +++ b/Client/mods/deathmatch/logic/CClientEntity.h @@ -76,6 +76,7 @@ enum eClientEntityType CCLIENTBROWSER, CCLIENTSEARCHLIGHT, CCLIENTIFP, + CCLIENTGIF, CCLIENTVECTORGRAPHIC, CCLIENTUNKNOWN, CCLIENTIMG, @@ -139,6 +140,7 @@ enum eCClientEntityClassTypes CLASS_CClientScreenSource, CLASS_CClientWebBrowser, CLASS_CClientVectorGraphic, + CLASS_CClientGif, CLASS_CClientWeapon, CLASS_CClientEffect, CLASS_CClientPointLights, diff --git a/Client/mods/deathmatch/logic/CClientGif.cpp b/Client/mods/deathmatch/logic/CClientGif.cpp new file mode 100644 index 0000000000..5cea3f9f4d --- /dev/null +++ b/Client/mods/deathmatch/logic/CClientGif.cpp @@ -0,0 +1,387 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CClientGif.h" + +#define GIF_ALLOCATE(pointer, size, release) pointer = (uint8_t*)realloc((release) ? 0 : pointer, (release) ? size : 0UL) +#define BIGE 0 +#define _SWAP(h) ((BIGE) ? ((uint16_t)(h << 8) | (h << 8)) : h) + +CClientGif::CClientGif(CClientManager* pManager, ElementID ID, CGifItem* pGifItem) : ClassInit(this), CClientTexture(pManager, ID, pGifItem) +{ + SetTypeName("gif"); + m_pManager = pManager; + m_uiStride = GetRenderItem()->m_uiSizeX * 4; + UpdateTick(); + m_pGifDisplay = std::make_unique(m_pManager->GetDisplayManager(), this); +} + +CClientGif::~CClientGif() +{ + Unlink(); +} + +void CClientGif::Register(std::vector>&& frms, std::vector& dls) +{ + m_vecFrames = std::move(frms); + m_vecDelays = std::move(dls); + m_vecDefaultDelays = m_vecDelays; + m_pGifDisplay->UpdateTexture(); +} + +void CClientGif::Next() +{ + if (m_uiShowing >= GetImageCount()) + { + m_uiShowing = 1; + } + else + { + m_uiShowing++; + } +} + +void CClientGif::Unlink() +{ + for (std::vector& frame : m_vecFrames) + { + frame.clear(); + } + m_vecFrames.clear(); + m_vecDelays.clear(); + m_vecDefaultDelays.clear(); + m_bIsDestoryed = true; + CClientRenderElement::Unlink(); +} + +CClientGifLoader::CClientGifLoader(SString& data) +{ + m_pBuffer = reinterpret_cast(data.data()); + m_lgSize = (long)((unsigned long)data.size()); +} + +CClientGifLoader::CClientGifLoader(uint8_t* data, unsigned long dataSize) +{ + m_pBuffer = data; + m_lgSize = (long)dataSize; +} + +CClientGifLoader::CClientGifLoader(char* data) +{ + m_pBuffer = reinterpret_cast(data); + m_lgSize = (long)((unsigned long)strlen(data)); +} + +void CClientGifLoader::CreateFrame(std::vector>& frames, std::vector& delays) +{ + long frameStride = m_lgWidth * 4; + long frameSize = frameStride * m_lgHeight; + std::vector frame; + for (long i = 0; i < frameSize; i++, frame.push_back(0)) + ; + uint32_t x; + uint32_t y; + uint32_t yoffset; + uint32_t iterator; + uint32_t inter; + uint32_t source; + uint32_t dstSource; + uint32_t* pDecoder; + uint32_t* pPrevious; +#define ARGB(i) \ + ((m_pAddress[i] == m_lgTransparent) ? 0 \ + : ((uint32_t)m_pPallete[m_pAddress[i]].r << 16 | (uint32_t)m_pPallete[m_pAddress[i]].g << 8 | \ + (uint32_t)m_pPallete[m_pAddress[i]].b << 0 | 0xff000000)) + if (!m_lgFrameNumber) + { + m_uiFrameCount = ((m_uiFrameCount < 0) ? -m_uiFrameCount : m_uiFrameCount) * m_lgHeight; + m_uiFrameCount = (m_uiFrameCount < 0xffff) ? m_uiFrameCount : 0xffff; + dstSource = (uint32_t)(m_lgWidth * m_lgHeight); + m_pPalleteDecoder = calloc(sizeof(uint32_t), dstSource); + m_pPalleteDecoderPrevious = calloc(sizeof(uint32_t), dstSource); + } + pDecoder = (uint32_t*)m_pPalleteDecoder; + dstSource = (uint32_t)(m_lgWidth * m_rcRect.top + m_rcRect.left); + inter = (!(iterator = m_lgInterlaced ? 0 : 4)) ? 4 : 5; + for (source = -1; iterator < inter; iterator++) + { + for (yoffset = 16U >> ((iterator > 1) ? iterator : 0), y = (8 >> iterator) & 7; y < (uint32_t)m_rcRect.height; y += yoffset) + { + for (x = 0; x < (uint32_t)m_rcRect.width; x++) + { + if (m_lgTransparent != (long)m_pAddress[++source]) + { + pDecoder[(uint32_t)m_lgWidth * y + x + dstSource] = ARGB(source); + } + } + } + } + memcpy((uint32_t*)frame.data(), pDecoder, sizeof(uint32_t) * (uint32_t)m_lgWidth * (uint32_t)m_lgHeight); + if ((m_lgMode == CGif_Previous) && !m_ulgLast) + { + m_rcRect.width = m_lgWidth; + m_rcRect.height = m_lgHeight; + m_lgMode = CGif_Background; + dstSource = 0; + } + else + { + m_ulgLast = (m_lgMode == CGif_Previous) ? m_ulgLast : (unsigned long)(m_lgFrameNumber + 1); + pDecoder = (uint32_t*)((m_lgMode == CGif_Previous) ? m_pPalleteDecoder : m_pPalleteDecoderPrevious); + pPrevious = (uint32_t*)((m_lgMode == CGif_Previous) ? m_pPalleteDecoderPrevious : m_pPalleteDecoder); + for (x = (uint32_t)(m_lgWidth * m_lgHeight); --x; pDecoder[x - 1] = pPrevious[x - 1]) + ; + } + if (m_lgMode == CGif_Background) + { + for (m_pAddress[0] = (uint8_t)((m_lgTransparent >= 0) ? m_lgTransparent : m_lgBackground), y = 0, pDecoder = (uint32_t*)m_pPalleteDecoder; + y < (uint32_t)m_rcRect.height; y++) + { + for (x = 0; x < (uint32_t)m_rcRect.width; x++) + { + pDecoder[(uint32_t)m_lgWidth * y + x + dstSource] = ARGB(0); + } + } + } +#undef ARGB + frames.push_back(std::move(frame)); + delays.push_back((int)m_lgDelay); +} + +void CClientGifLoader::Load(std::vector>& frames, std::vector& delays, long skip) +{ + const long Blen = (1 << 12) * sizeof(uint32_t); + const uint8_t extensionHeaderMark = 0x21; + const uint8_t frameHeaderMark = 0x2c; + const uint8_t endOfBufferMark = 0x3b; + const uint8_t graphicControlMark = 0xf9; + const uint8_t appMetadataMark = 0xff; + if (!m_pBuffer) + { + m_bError = true; + return; + } +#pragma pack(push, 1) + struct GlobalHeader + { + uint8_t head[6]; + uint16_t width; + uint16_t height; + uint8_t flags; + uint8_t background; + uint8_t aspectRatio; + }* header = (struct GlobalHeader*)m_pBuffer; + struct FrameHeader + { + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; + uint8_t flags; + }* frameHeader; + struct GraphicControlHeader + { + uint8_t flags; + uint16_t delay; + uint8_t transparent; + }* graphicControl = 0; +#pragma pack(pop) + long desc; + long blen; + uint8_t* decoder; + if ( // check if header is : `GIF89a` or `GIF87a` + !header || (m_lgSize <= (long)sizeof(*header)) || (*(decoder = header->head) != 71) || decoder[1] != 73 || decoder[2] != 70 || decoder[3] != 56 || + skip < 0 || (decoder[4] != 55 && decoder[4] != 57) || decoder[5] != 97) + { + m_bError = true; + return; + } + m_strFormat = decoder[4] ? "GIF87a" : "GIF89a"; + decoder = (uint8_t*)(header + 1) + LoadHeader(header->flags, 0, 0, 0, 0, 0L) * 3L; + if ((m_lgSize -= decoder - (uint8_t*)header) <= 0) + { + m_bError = true; + return; + } + m_lgWidth = _SWAP(header->width); + m_lgHeight = _SWAP(header->height); + for (m_pAddress = decoder, m_lgBackground = header->background, blen = --m_lgSize; blen >= 0 && ((desc = *m_pAddress++) != endOfBufferMark); + blen = SkipChunk(&m_pAddress, blen) - 1) + { + if (desc == frameHeaderMark) + { + frameHeader = (struct FrameHeader*)m_pAddress; + if (LoadHeader(header->flags, &m_pAddress, (void**)&m_pPallete, frameHeader->flags, &blen, sizeof(*frameHeader)) <= 0) + { + break; + } + m_rcRect.width = _SWAP(frameHeader->width); + m_rcRect.height = _SWAP(frameHeader->height); + m_rcRect.left = (m_rcRect.width > m_rcRect.left) ? m_rcRect.width : m_rcRect.left; + m_rcRect.top = (m_rcRect.height > m_rcRect.top) ? m_rcRect.height : m_rcRect.top; + m_lgFrameNumber++; + } + } + blen = m_rcRect.left * m_rcRect.top * (long)sizeof(*m_pAddress); + GIF_ALLOCATE(m_pAddress, (unsigned long)(blen + Blen + 2), 1); + m_uiFrameCount = (desc != endOfBufferMark) ? -m_lgFrameNumber : m_lgFrameNumber; + for (m_pAddress += Blen, m_lgFrameNumber = -1; blen && (skip < ((m_uiFrameCount < 0) ? -m_uiFrameCount : m_uiFrameCount)) && m_lgSize >= 0; + m_lgSize = (desc != endOfBufferMark) ? ((desc != frameHeaderMark) || (skip > m_lgFrameNumber)) ? SkipChunk(&decoder, m_lgSize) - 1 : m_lgSize - 1 : -1) + { + if ((desc = *decoder++) == frameHeaderMark) + { // found a frame + m_lgInterlaced = !!((frameHeader = (struct FrameHeader*)decoder)->flags & 0x40); + *(void**)&m_pPallete = (void*)(header + 1); + m_lgPalleteSize = LoadHeader(header->flags, &decoder, (void**)&m_pPallete, frameHeader->flags, &m_lgSize, sizeof(*frameHeader)); + if ((skip <= ++m_lgFrameNumber) && ((m_lgPalleteSize <= 0) || LoadFrame(&decoder, &m_lgSize, m_pAddress, m_pAddress + blen) < 0)) + { + m_lgSize = -(m_lgFrameNumber--) - 1; // failed to load frame + } + else if (skip <= m_lgFrameNumber) + { + m_rcRect.left = _SWAP(frameHeader->x); + m_rcRect.top = _SWAP(frameHeader->y); + m_rcRect.width = _SWAP(frameHeader->width); + m_rcRect.height = _SWAP(frameHeader->height); + m_lgDelay = (graphicControl) ? _SWAP(graphicControl->delay) : 0; + m_lgTransparent = (graphicControl && (graphicControl->flags & 0x01)) ? graphicControl->transparent : -1; + m_lgDelay = ((graphicControl && (graphicControl->flags & 0x02)) ? -m_lgDelay - 1 : m_lgDelay) * 10; + m_lgMode = (graphicControl && !(graphicControl->flags & 0x10)) ? (graphicControl->flags & 0x0c) >> 2 : CGif_None; + graphicControl = 0; + CreateFrame(frames, delays); // creating frame plain + } + } + else if (desc == extensionHeaderMark) + { // found an extension + if (*decoder == graphicControlMark) + { + graphicControl = (struct GraphicControlHeader*)(decoder + 1 + 1); + } + } + } + m_pAddress -= Blen; + GIF_ALLOCATE(m_pAddress, (unsigned long)(blen + Blen + 2), 0); +} + +bool CClientGifLoader::operator!() +{ + return m_pBuffer ? m_bError : false; +} + +long CClientGifLoader::SkipChunk(uint8_t** buffer, long size) +{ + long skip; + for (skip = 2, ++size, ++(*buffer); ((size -= skip) > 0) && (skip > 1); *buffer += (skip = 1 + **buffer)) + ; + return size; +} + +long CClientGifLoader::LoadHeader(unsigned globalFlags, uint8_t** buffer, void** pallete, unsigned state, long* size, long length) +{ + if (length && (!(*buffer += length) || ((*size -= length) <= 0))) + { + return -2; + } + if (length && (state & 0x80)) + { + *pallete = *buffer; + *buffer += (length = 2 << (state & 7)) * 3L; + return ((*size -= length * 3L) > 0) ? length : -1; + } + return (globalFlags & 0x80) ? (2 << (globalFlags & 7)) : 0; +} + +long CClientGifLoader::LoadFrame(uint8_t** buffer, long* size, uint8_t* address, uint8_t* blen) +{ + const long headerLength = sizeof(uint16_t); + const long tableLength = 1 << 12; + uint16_t accumulator; + uint16_t mask; + long lastCodeTable; + long iterator; + long previous; + long current; + long minLZWSize; + long currentLZWSize; + long blockSequense; + long bitSize; + uint32_t* code = (uint32_t*)address - tableLength; + if ((--(*size) <= headerLength) || !*++(*buffer)) + { + return -4; + } + mask = (uint16_t)((1 << (currentLZWSize = (minLZWSize = *(*buffer - 1)) + 1)) - 1); + if (minLZWSize < 2 || minLZWSize > 8) + { + return -3; + } + if ((lastCodeTable = (1L << minLZWSize)) != (mask & _SWAP(*(uint16_t*)(*buffer + 1)))) + { + return -2; + } + for (current = ++lastCodeTable; current; code[--current] = 0) + ; + for (--(*size), bitSize = -currentLZWSize, previous = current = 0; ((*size -= (blockSequense = *(*buffer)++) + 1) >= 0) && blockSequense; + *buffer += blockSequense) + { + for (; blockSequense > 0; blockSequense -= headerLength, *buffer += headerLength) + { + for (accumulator = (uint16_t)(_SWAP(*(uint16_t*)*buffer) & ((blockSequense < headerLength) ? ((1U << (8 * blockSequense)) - 1U) : ~0U)), + current |= accumulator << (currentLZWSize + bitSize), accumulator = (uint16_t)(accumulator >> -bitSize), + bitSize += 8 * ((blockSequense < headerLength) ? blockSequense : headerLength); + bitSize >= 0; bitSize -= currentLZWSize, previous = current, current = accumulator, accumulator = (uint16_t)(accumulator >> currentLZWSize)) + { + if (((current &= mask) & ~1L) == (1L << minLZWSize)) + { + if (~(lastCodeTable = current + 1) & 1) + { // end of frame data + return (*((*buffer += blockSequense + 1) - 1)) ? -1 : 1; + } + mask = (uint16_t)((1 << (currentLZWSize = minLZWSize + 1)) - 1); + } + else + { + if (lastCodeTable < tableLength) + { + if ((lastCodeTable == mask) && (lastCodeTable < tableLength - 1)) + { + mask = (uint16_t)(mask + mask + 1); + currentLZWSize++; + } + code[lastCodeTable] = (uint32_t)previous + (code[previous] & 0xfff000); + } + previous = (long)code[iterator = (lastCodeTable > current) ? current : previous]; + if ((address += (previous = (previous >> 12) & 0xfff)) > blen) + { + continue; + } + for (previous++; (iterator &= 0xfff) >> minLZWSize; *address-- = (uint8_t)((iterator = (long)code[iterator]) >> 24)) + ; + (address += previous)[-previous] = (uint8_t)iterator; + if (lastCodeTable < tableLength) + { + if (lastCodeTable == current) + { + *address++ = (uint8_t)iterator; + } + else if (lastCodeTable < current) + { + return -5; // wrong code + } + code[lastCodeTable++] += ((uint32_t)iterator << 24) + 0x1000; + } + } + } + } + } + return (++(*size) >= 0) ? 0 : -4; // recoverable error +} + +#undef _SWAP diff --git a/Client/mods/deathmatch/logic/CClientGif.h b/Client/mods/deathmatch/logic/CClientGif.h new file mode 100644 index 0000000000..685d318ad7 --- /dev/null +++ b/Client/mods/deathmatch/logic/CClientGif.h @@ -0,0 +1,140 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CClientGifDisplay.h" + +enum +{ + CGif_None = 0, + CGif_Current = 1, + CGif_Background = 2, + CGif_Previous = 3 +}; + +class CClientGifRect +{ +public: + uint left = 0; + uint top = 0; + uint width = 0; + uint height = 0; + bool valid = false; + CClientGifRect() = default; + CClientGifRect(const uint& l, const uint& t, const uint& w, const uint& h) : left(l), top(t), width(w), height(h) { valid = true; } +}; + +class CClientGifLoader +{ +public: + CClientGifLoader() = default; + CClientGifLoader(SString& data); + CClientGifLoader(uint8_t* data, unsigned long dataSize); + CClientGifLoader(char* data); + + void CreateFrame(std::vector>& frames, std::vector& delays); + void Load(std::vector>& frames, std::vector& delays, long skip); + uint8_t* GetBuffer() { return m_pBuffer; } + long& GetWidth() { return m_lgWidth; } + long& GetHeight() { return m_lgHeight; } + long& GetDelay() { return m_lgDelay; } + long& GetFrameNumber() { return m_lgFrameNumber; } + long& GetFrameCount() { return m_uiFrameCount; } + SString GetFormat() { return m_strFormat; } + bool operator!(); + + static long SkipChunk(uint8_t** buffer, long size); + static long LoadHeader(unsigned globalFlags, uint8_t** buffer, void** pallete, unsigned state, long* size, long length); + static long LoadFrame(uint8_t** buffer, long* size, uint8_t* address, uint8_t* blen); + +private: + struct + { + uint8_t r; + uint8_t g; + uint8_t b; + }* m_pPallete; + SString m_strFormat = "Undefined"; + uint8_t* m_pBuffer = nullptr; + uint8_t* m_pAddress = nullptr; + void* m_pPalleteDecoder = nullptr; + void* m_pPalleteDecoderPrevious = nullptr; + long m_lgSize = 0; + long m_lgWidth = 0; + long m_lgHeight = 0; + long m_lgPalleteSize = 0; + long m_lgBackground = 0; + long m_lgTransparent = 0; + long m_lgInterlaced = 0; + long m_lgDelay = 0; + long m_lgFrameNumber = 0; + long m_uiFrameCount = 0; + long m_lgMode = CGif_None; + unsigned long m_ulgLast = 0; + CClientGifRect m_rcRect = {0, 0, 0, 0}; + bool m_bError = false; +}; + +class CClientGif final : public CClientTexture +{ + DECLARE_CLASS(CClientGif, CClientTexture); + +public: + CClientGif(CClientManager* pManager, ElementID id, CGifItem* pGifItem); + ~CClientGif(); + + virtual void Unlink() override; + void Register(std::vector>&& frms, std::vector& dls); + + CResource* GetResource() { return m_pResource; } + void SetResource(CResource* resource) { m_pResource = resource; } + eClientEntityType GetType() const { return CCLIENTGIF; } + CGifItem* GetRenderItem() const { return static_cast(m_pRenderItem); } + CClientGifDisplay* GetDisplay() const { return m_pGifDisplay.get(); } + std::vector> GetFrames() const { return m_vecFrames; } + uint GetStride() const { return m_uiStride; } + uint GetShowingFrame() const { return m_uiShowing; } + uint GetFrameDelay(const uint& id) const { return m_vecDelays[(id < 1 ? m_uiShowing : (id > GetImageCount() ? m_uiShowing : id)) - 1]; } + uint GetFrameDefaultDelay(const uint& id) const { return m_vecDefaultDelays[(id < 1 ? m_uiShowing : (id > GetImageCount() ? m_uiShowing : id)) - 1]; } + int GetImageCount() const { return m_vecFrames.size(); } + double GetTick() const { return m_dbTick; } + unsigned char* GetFrame() { return m_uiShowing <= GetImageCount() ? m_vecFrames[m_uiShowing - 1].data() : nullptr; } + SString GetFormat() const { return m_strFormat; } + uint GetFrameCount() const { return m_uiFrameCount; } + + void SetFrameDelay(const uint& id, const uint32_t& delay) { m_vecDelays[(id < 1 ? m_uiShowing : (id > GetImageCount() ? (m_uiShowing) : id)) - 1] = delay; } + void SetFormat(const SString& fmt) { if (!fmt.empty()) m_strFormat = fmt; } + void SetFrameCount(const uint& count) { m_uiFrameCount = count; } + void SetFrame(const int& frame) { if (frame <= GetImageCount()) m_uiShowing = 1; } + + void UpdateTick() { m_dbTick = (double)GetTickCount64_(); } + void Play() { m_bPlaying = true; } + void Stop() { m_bPlaying = false; } + void Next(); + void NavigateToThumbnail() { m_uiShowing = 1; } + + bool IsPlaying() const { return m_bPlaying; } + bool IsDestoryed() const { return m_bIsDestoryed; } + +private: + CResource* m_pResource = nullptr; + CClientManager* m_pManager; + std::unique_ptr m_pGifDisplay; + SString m_strFormat = "Undefined"; + std::vector> m_vecFrames; + std::vector m_vecDefaultDelays; + std::vector m_vecDelays; + uint m_uiFrameCount = 0; + uint m_uiStride; + uint m_uiShowing = 1; + double m_dbTick = 0; + bool m_bPlaying = false; + bool m_bIsDestoryed = false; +}; diff --git a/Client/mods/deathmatch/logic/CClientGifDisplay.cpp b/Client/mods/deathmatch/logic/CClientGifDisplay.cpp new file mode 100644 index 0000000000..de92a91c36 --- /dev/null +++ b/Client/mods/deathmatch/logic/CClientGifDisplay.cpp @@ -0,0 +1,113 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CClientGifDisplay.h" +#include "CClientGif.h" + +CClientGifDisplay::CClientGifDisplay(CClientDisplayManager* pDisplayManager, CClientGif* pGif, int ID) : CClientDisplay(pDisplayManager, ID) +{ + m_pGif = pGif; + m_bVisible = true; + m_bIsCleared = false; +} + +void CClientGifDisplay::Render() +{ + if (!m_pGif || m_pGif->IsDestoryed()) + { + return; + } + if (!m_bVisible) + { + if (!IsCleared()) + { + ClearTexture(); + } + return; + } + else if (IsCleared()) + { + m_bIsCleared = false; + } + if (m_pGif->IsPlaying()) + { + if ((double)GetTickCount64_() >= m_pGif->GetTick() + m_pGif->GetFrameDelay(-1)) + { + m_pGif->Next(); + m_pGif->UpdateTick(); + UpdateTexture(); + } + } +} + +void CClientGifDisplay::UpdateTexture() +{ + if (!m_pGif || m_pGif->IsDestoryed()) + { + return; + } + CGifItem* pGifItem = m_pGif->GetRenderItem(); + if (!pGifItem) + { + return; + } + IDirect3DSurface9* surface = pGifItem->m_pD3DRenderTargetSurface; + if (!surface) + { + return; + } + IDirect3DDevice9* device = pGifItem->m_pDevice; + // Lock Surface + D3DLOCKED_RECT LockedRect; + if (SUCCEEDED(surface->LockRect(&LockedRect, nullptr, D3DLOCK_DISCARD))) + { + // Unlock Surface + unsigned char* frame = m_pGif->GetFrame(); + if (frame) + { + uint stride = m_pGif->GetStride(); + auto surfaceData = static_cast(LockedRect.pBits); + auto data = static_cast(frame); + for (int i = 0; i < pGifItem->m_uiSizeY; i++) + { + memcpy(surfaceData, data, stride); + data += stride; + surfaceData += LockedRect.Pitch; + } + } + surface->UnlockRect(); + } +} + +void CClientGifDisplay::ClearTexture() +{ + if (!m_pGif || m_pGif->IsDestoryed()) + { + return; + } + CGifItem* pGif = m_pGif->GetRenderItem(); + if (!pGif) + { + return; + } + IDirect3DSurface9* surface = pGif->m_pD3DRenderTargetSurface; + if (!surface) + { + return; + } + IDirect3DDevice9* device = pGif->m_pDevice; + // Lock Surface + D3DLOCKED_RECT LockedRect; + surface->LockRect(&LockedRect, nullptr, 0); + device->ColorFill(surface, nullptr, D3DCOLOR_RGBA(0, 0, 0, 0)); + // Unlock Surface + surface->UnlockRect(); + m_bIsCleared = true; +} diff --git a/Client/mods/deathmatch/logic/CClientGifDisplay.h b/Client/mods/deathmatch/logic/CClientGifDisplay.h new file mode 100644 index 0000000000..2f7b6f22a2 --- /dev/null +++ b/Client/mods/deathmatch/logic/CClientGifDisplay.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +class CClientGifDisplay; + +#pragma once + +#include "CClientDisplay.h" +#include "CClientDisplayManager.h" + +class CClientGifDisplay final : public CClientDisplay +{ + friend class CClientDisplayManager; + +public: + CClientGifDisplay(CClientDisplayManager* pDisplayManager, CClientGif* pGif, int ID = DISPLAY_GIF); + ~CClientGifDisplay() = default; + eDisplayType GetType() { return DISPLAY_GIF; } + void Render(); + void UpdateTexture(); + void ClearTexture(); + bool IsCleared() { return m_bIsCleared; } + +private: + CClientGif* m_pGif; + bool m_bIsCleared; +}; diff --git a/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp b/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp index 7cda0784e5..efd97292fa 100644 --- a/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp +++ b/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "CClientVectorGraphic.h" +#include "CClientGif.h" //////////////////////////////////////////////////////////////// // @@ -29,6 +30,7 @@ CClientRenderElementManager::CClientRenderElementManager(CClientManager* pClient m_uiStatsScreenSourceCount = 0; m_uiStatsWebBrowserCount = 0; m_uiStatsVectorGraphicCount = 0; + m_uiStatsGifCount = 0; } //////////////////////////////////////////////////////////////// @@ -273,6 +275,35 @@ CClientVectorGraphic* CClientRenderElementManager::CreateVectorGraphic(uint widt return pVectorGraphicElement; } +//////////////////////////////////////////////////////////////// +// +// CClientRenderElementManager::CreateGif +// +// +// +//////////////////////////////////////////////////////////////// +CClientGif* CClientRenderElementManager::CreateGif(uint width, uint height) +{ + // Create the item + CGifItem* pGifItem = m_pRenderItemManager->CreateGif(width, height); + + // Check create worked + if (!pGifItem) + { + return nullptr; + } + + // Create the element + CClientGif* pGifElement = new CClientGif(m_pClientManager, INVALID_ELEMENT_ID, pGifItem); + + // Add to this manager's list + MapSet(m_ItemElementMap, pGifElement->GetRenderItem(), pGifElement); + + m_uiStatsGifCount++; + + return pGifElement; +} + //////////////////////////////////////////////////////////////// // // CClientRenderElementManager::FindAutoTexture @@ -343,6 +374,8 @@ void CClientRenderElementManager::Remove(CClientRenderElement* pElement) m_uiStatsWebBrowserCount--; else if (pElement->IsA(CClientVectorGraphic::GetClassId())) m_uiStatsVectorGraphicCount--; + else if (pElement->IsA(CClientGif::GetClassId())) + m_uiStatsGifCount--; else if (pElement->IsA(CClientTexture::GetClassId())) m_uiStatsTextureCount--; diff --git a/Client/mods/deathmatch/logic/CClientRenderElementManager.h b/Client/mods/deathmatch/logic/CClientRenderElementManager.h index ff305cef67..d06caae073 100644 --- a/Client/mods/deathmatch/logic/CClientRenderElementManager.h +++ b/Client/mods/deathmatch/logic/CClientRenderElementManager.h @@ -18,6 +18,7 @@ class CClientRenderTarget; class CClientScreenSource; class CClientWebBrowser; class CClientVectorGraphic; +class CClientGif; class CClientRenderElementManager { @@ -36,6 +37,7 @@ class CClientRenderElementManager CClientScreenSource* CreateScreenSource(uint uiSizeX, uint uiSizeY); CClientWebBrowser* CreateWebBrowser(uint uiSizeX, uint uiSizeY, bool bIsLocal, bool bTransparent); CClientVectorGraphic* CreateVectorGraphic(uint width, uint height); + CClientGif* CreateGif(uint width, uint height); CClientTexture* FindAutoTexture(const SString& strFullFilePath, const SString& strUniqueName); void Remove(CClientRenderElement* pElement); @@ -47,6 +49,7 @@ class CClientRenderElementManager uint GetScreenSourceCount() { return m_uiStatsScreenSourceCount; } uint GetWebBrowserCount() { return m_uiStatsWebBrowserCount; } uint GetVectorGraphicCount() { return m_uiStatsVectorGraphicCount; } + uint GetGifCount() { return m_uiStatsGifCount; } protected: CClientManager* m_pClientManager; @@ -61,4 +64,5 @@ class CClientRenderElementManager uint m_uiStatsScreenSourceCount; uint m_uiStatsWebBrowserCount; uint m_uiStatsVectorGraphicCount; + uint m_uiStatsGifCount; }; diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index 4fbb4fd53f..742acff594 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -517,6 +517,11 @@ inline SString GetClassTypeName(CClientVectorGraphic*) return "svg"; } +inline SString GetClassTypeName(CClientGif*) +{ + return "gif"; +} + // // CResource from userdata // diff --git a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp index 6b794be016..89073655e6 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp @@ -27,6 +27,8 @@ SString CLuaMain::ms_strExpectedUndumpHash; #include "luascripts/exports.lua.h" #include "luascripts/inspect.lua.h" +#include "../luadefs/CLuaGifDefs.h" + CLuaMain::CLuaMain(CLuaManager* pLuaManager, CResource* pResourceOwner, bool bEnableOOP) { // Initialise everything to be setup in the Start function @@ -127,8 +129,8 @@ void CLuaMain::InitClasses(lua_State* luaVM) CLuaVehicleDefs::AddClass(luaVM); CLuaWaterDefs::AddClass(luaVM); CLuaWeaponDefs::AddClass(luaVM); + CLuaGifDefs::AddClass(luaVM); CLuaBuildingDefs::AddClass(luaVM); - CLuaShared::AddClasses(luaVM); } diff --git a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp index c2a28809af..b8974b4a8c 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -13,6 +13,7 @@ #include "../luadefs/CLuaFireDefs.h" #include "../luadefs/CLuaClientDefs.h" #include "../luadefs/CLuaVectorGraphicDefs.h" +#include "../luadefs/CLuaGifDefs.h" using std::list; @@ -273,6 +274,7 @@ void CLuaManager::LoadCFunctions() CLuaTeamDefs::LoadFunctions(); CLuaTimerDefs::LoadFunctions(); CLuaVectorGraphicDefs::LoadFunctions(); + CLuaGifDefs::LoadFunctions(); CLuaVehicleDefs::LoadFunctions(); CLuaWaterDefs::LoadFunctions(); CLuaWeaponDefs::LoadFunctions(); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaGifDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaGifDefs.cpp new file mode 100644 index 0000000000..158bf974cd --- /dev/null +++ b/Client/mods/deathmatch/logic/luadefs/CLuaGifDefs.cpp @@ -0,0 +1,267 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "lua/CLuaFunctionParser.h" +#include "CLuaGifDefs.h" +#include "CScriptFile.h" + +#ifndef DEFAULT_MAX_FILESIZE + #define DEFAULT_MAX_FILESIZE 52428800 +#endif + +void CLuaGifDefs::LoadFunctions() +{ + constexpr static const std::pair functions[]{ + {"gifCreate", ArgumentParser}, + {"gifPlay", ArgumentParser}, + {"gifStop", ArgumentParser}, + {"gifSetProperty", ArgumentParser}, + {"gifGetProperty", ArgumentParser}, + {"gifIsPlaying", ArgumentParser}, + }; + for (const auto& [name, func] : functions) + CLuaCFunctions::AddFunction(name, func); +} + +void CLuaGifDefs::AddClass(lua_State* luaVM) +{ + lua_newclass(luaVM); + lua_classfunction(luaVM, "play", "gifPlay"); + lua_classfunction(luaVM, "stop", "gifStop"); + lua_classfunction(luaVM, "setProperty", "gifSetProperty"); + lua_classfunction(luaVM, "getProperty", "gifGetProperty"); + lua_classfunction(luaVM, "isPlaying", "GifIsPlaying"); + lua_registerclass(luaVM, "Gif"); +} + +CClientGif* CLuaGifDefs::GifCreate(lua_State* luaVM, std::string pathOrRawdata) +{ + SString buffer = pathOrRawdata; + CLuaMain& luaMain = lua_getownercluamain(luaVM); + CResource* resource = luaMain.GetResource(); + SString path; + SString metaPath; + SString fileBuffer; + SString lastPath = buffer; + SString format = buffer.substr(0, 6); + CClientGifLoader loader; + if (format == "GIF87a" || format == "GIF89a") + { + loader = CClientGifLoader(buffer); + } + else if (CResourceManager::ParseResourcePathInput(buffer, resource, &path, &metaPath)) + { + if (FileExists(path)) + { + eAccessType access = buffer[0] == '@' ? eAccessType::ACCESS_PRIVATE : eAccessType::ACCESS_PUBLIC; + CScriptFile* file = new CScriptFile(resource->GetScriptID(), metaPath, DEFAULT_MAX_FILESIZE, access); + if (file->Load(resource, CScriptFile::MODE_READ)) + { + file->SetLuaDebugInfo(g_pClientGame->GetScriptDebugging()->GetLuaDebugInfo(luaVM)); + long size = file->GetSize(); + long bytesRead = file->Read(size, fileBuffer); + if (bytesRead >= 0) + { + loader = CClientGifLoader(fileBuffer); + } + else if (bytesRead == -2) + { + m_pScriptDebugging->LogWarning(luaVM, "out of memory"); + } + else + { + m_pScriptDebugging->LogError(luaVM, SString("error while reading file [%s]", lastPath.c_str()).c_str()); + } + file->Unload(); + m_pElementDeleter->Delete(file); + } + else + { + delete file; + m_pScriptDebugging->LogError(luaVM, SString("couldn't load file [%s]", lastPath.c_str()).c_str()); + return nullptr; + } + } + else + { + m_pScriptDebugging->LogError(luaVM, SString("file [%s] doesn't exists!", lastPath.c_str()).c_str()); + return nullptr; + } + } + else + { + loader = CClientGifLoader(buffer); + } + std::vector> frames; + std::vector delays; + loader.Load(frames, delays, 0L); + if (!loader) + { + m_pScriptDebugging->LogError(luaVM, "wrong file format"); + return nullptr; + } + if (frames.size() < 1) + { + m_pScriptDebugging->LogError(luaVM, "gif has no frames"); + return nullptr; + } + uint width = (uint)loader.GetWidth(); + uint height = (uint)loader.GetHeight(); + if (width < 1 || height < 1) + { + m_pScriptDebugging->LogError(luaVM, "gif must be 1x1 at least"); + return nullptr; + } + CClientGif* gif = g_pClientGame->GetManager()->GetRenderElementManager()->CreateGif(width, height); + if (!gif) + { + m_pScriptDebugging->LogError(luaVM, "error while creating gif"); + return nullptr; + } + gif->SetParent(resource->GetResourceDynamicEntity()); + gif->SetResource(resource); + gif->SetFrameCount(frames.size()); + gif->Register(std::move(frames), std::move(delays)); + gif->SetFormat(loader.GetFormat()); + return gif; +} + +bool CLuaGifDefs::GifPlay(CClientGif* gif) +{ + gif->Play(); + return true; +} + +bool CLuaGifDefs::GifStop(CClientGif* gif) +{ + gif->Stop(); + return true; +} + +bool CLuaGifDefs::GifSetProperty(lua_State* luaVM, CClientGif* gif, std::string property, std::variant value, std::optional frame) +{ + bool valIsNumber = false; + int numberValue = -1; + if (std::holds_alternative(value)) + { + valIsNumber = true; + numberValue = std::get(value); + if (numberValue < 0) + { + m_pScriptDebugging->LogError(luaVM, "property value can't be a negative number"); + return false; + } + } + if (property.empty()) + { + m_pScriptDebugging->LogError(luaVM, "property can't be empty"); + return false; + } + if (frame.has_value()) + { + int frameCount = gif->GetFrameCount(); + if (frame > frameCount) + { + m_pScriptDebugging->LogError(luaVM, "frame doesn't exist"); + return false; + } + if (property == "delay") + { + if (!valIsNumber) + { + m_pScriptDebugging->LogError(luaVM, "property `delay` must be a number value"); + return false; + } + gif->SetFrameDelay(frame.value(), numberValue); + return true; + } + else + { + m_pScriptDebugging->LogError(luaVM, "property doesn't exist for frame"); + } + } + else + { + if (property == "showing_frame") + { + if (!valIsNumber) + { + m_pScriptDebugging->LogError(luaVM, "property `showing_frame` must be a number value"); + return false; + } + gif->SetFrame(numberValue); + return true; + } + else + { + m_pScriptDebugging->LogError(luaVM, "property doens't exist for gif"); + } + } + return false; +} + +std::variant CLuaGifDefs::GifGetProperty(lua_State* luaVM, CClientGif* gif, std::string property, std::optional frame) +{ + if (property.empty()) + { + m_pScriptDebugging->LogError(luaVM, "property can't be empty"); + return false; + } + if (frame.has_value()) + { + int frameCount = gif->GetFrameCount(); + if (frame > frameCount) + { + m_pScriptDebugging->LogError(luaVM, "frame doesn't exist"); + return false; + } + if (property == "delay") + { + return (int)gif->GetFrameDelay(frame.value()); + } + else if (property == "default_delay") + { + return (int)gif->GetFrameDefaultDelay(frame.value()); + } + else + { + m_pScriptDebugging->LogError(luaVM, "property doesn't exist for frame"); + } + } + else + { + if (property == "showing_frame") + { + return (int)gif->GetShowingFrame(); + } + else if (property == "frame_count") + { + return (int)gif->GetFrameCount(); + } + else if (property == "format") + { + return (std::string)gif->GetFormat(); + } + else if (property == "tick") + { + return (int)gif->GetTick(); + } + else + { + m_pScriptDebugging->LogError(luaVM, "property doesn't exist for gif"); + } + } + return false; +} + +bool CLuaGifDefs::GifIsPlaying(CClientGif* gif) +{ + return gif->IsPlaying(); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaGifDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaGifDefs.h new file mode 100644 index 0000000000..e520796fca --- /dev/null +++ b/Client/mods/deathmatch/logic/luadefs/CLuaGifDefs.h @@ -0,0 +1,29 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CLuaDefs.h" +#include "CClientGif.h" + +class CLuaGifDefs : public CLuaDefs +{ +public: + static void LoadFunctions(); + static void AddClass(lua_State* luaVM); + + static CClientGif* GifCreate(lua_State* luaVM, std::string pathOrRawdata); + static bool GifPlay(CClientGif* gif); + static bool GifStop(CClientGif* gif); + + static bool GifSetProperty(lua_State* luaVM, CClientGif* gif, std::string property, std::variant value, std::optional frame); + static std::variant GifGetProperty(lua_State* luaVM, CClientGif* gif, std::string property, std::optional frame); + + static bool GifIsPlaying(CClientGif* gif); +}; diff --git a/Client/sdk/core/CRenderItemManagerInterface.h b/Client/sdk/core/CRenderItemManagerInterface.h index 255c6ee0d3..b92e86d5a8 100644 --- a/Client/sdk/core/CRenderItemManagerInterface.h +++ b/Client/sdk/core/CRenderItemManagerInterface.h @@ -40,6 +40,7 @@ enum eAspectRatio; class CWebViewInterface; class CEffectTemplate; class CVectorGraphicItem; +class CGifItem; #define RDEFAULT ((uint)-1) @@ -164,6 +165,7 @@ class CRenderItemManagerInterface virtual CScreenSourceItem* CreateScreenSource(uint uiSizeX, uint uiSizeY) = 0; virtual CWebBrowserItem* CreateWebBrowser(uint uiSizeX, uint uiSizeY) = 0; virtual CVectorGraphicItem* CreateVectorGraphic(uint uiSizeX, uint uiSizeY) = 0; + virtual CGifItem* CreateGif(uint uiSizeX, uint uiSizeY) = 0; virtual bool SetRenderTarget(CRenderTargetItem* pItem, bool bClear) = 0; virtual void EnableSetRenderTargetOldVer(bool bEnable) = 0; virtual bool IsSetRenderTargetEnabledOldVer() = 0; @@ -240,6 +242,7 @@ enum eRenderItemClassTypes CLASS_CShaderInstance, CLASS_CTextureItem, CLASS_CVectorGraphicItem, + CLASS_CGifItem, CLASS_CFileTextureItem, CLASS_CRenderTargetItem, CLASS_CScreenSourceItem, @@ -494,6 +497,28 @@ class CVectorGraphicItem : public CTextureItem IDirect3DSurface9* m_pD3DRenderTargetSurface; }; +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +// +// CGifItem - CClientGif to texture +// +class CGifItem : public CTextureItem +{ + DECLARE_CLASS(CGifItem, CTextureItem) + CGifItem() : ClassInit(this) {} + virtual void PostConstruct(CRenderItemManager* pRenderItemManager, uint width, uint height); + virtual void PreDestruct(); + virtual bool IsValid(); + virtual void OnLostDevice(); + virtual void OnResetDevice(); + void CreateUnderlyingData(); + void ReleaseUnderlyingData(); + void UpdateTexture(); + virtual void Resize(const CVector2D& size); + + IDirect3DSurface9* m_pD3DRenderTargetSurface; +}; + //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //