#include "pch.h" ////////////////////////////////////////////////////////////////////////////// // // FrameImage // ////////////////////////////////////////////////////////////////////////////// class FrameImage : public Image { private: TRef m_pimageBackground; TRef m_psurface; TRef m_pobjectMemory; int m_nFrame; int m_frameCurrent; DWORD* m_pdwOffsets; BYTE* m_prle; public: FrameImage( Image* pimageBackground, Number* pframe, int nFrames, DWORD* pdwOffsets, BYTE* prle, IObject* pobjectMemory ) : Image(pframe, pimageBackground), m_pimageBackground(pimageBackground), m_nFrame(nFrames), m_pdwOffsets(pdwOffsets), m_prle(prle), m_pobjectMemory(pobjectMemory) { TRef psurfaceBackground = m_pimageBackground->GetSurface(); m_psurface = psurfaceBackground->CreateCompatibleSurface( psurfaceBackground->GetSize(), SurfaceType2D() ); m_psurface->BitBlt(WinPoint(0, 0), psurfaceBackground); m_frameCurrent = 0; m_bounds.SetRect( Rect( Point(0, 0), Point::Cast(m_psurface->GetSize()) ) ); } ~FrameImage() { if (m_pobjectMemory == NULL) { delete[] m_pdwOffsets; delete[] m_prle; } } ////////////////////////////////////////////////////////////////////////////// // // FrameImage methods // ////////////////////////////////////////////////////////////////////////////// Number* GetFrameNumber() { return Number::Cast(GetChild(0)); } int GetFrame() { return bound( (int)(GetFrameNumber()->GetValue() * m_nFrame), 0, m_nFrame - 1 ); } void PlayRLE(BYTE* pd, BYTE* prle, BYTE* pend) { while (prle < pend) { WORD word = *(WORD*)prle; int length = word & RLELengthMask; prle += 2; switch (word & RLEMask) { case RLEMaskFill: { for (int index = length; index > 0; index --) { (*(BYTE*)pd) = (*(BYTE*)pd) ^ (*(BYTE*)prle); pd += 1; prle += 1; } } break; case RLEMaskBYTE: { BYTE byte = *(BYTE*)prle; prle += 1; for (int index = length; index > 0; index --) { (*(BYTE*)pd) = (*(BYTE*)pd) ^ byte; pd += 1; } } break; case RLEMaskWORD: { WORD word = *(WORD*)prle; prle += 2; for (int index = length; index > 0; index --) { (*(WORD*)pd) = (*(WORD*)pd) ^ word; pd += 2; } } break; case RLEMaskDWORD: { DWORD dword = *(DWORD*)prle; prle += 4; for (int index = length; index > 0; index --) { (*(DWORD*)pd) = (*(DWORD*)pd) ^ dword; pd += 4; } } break; } } } void UpdateFrame() { int newFrame = GetFrame(); if (m_frameCurrent != newFrame) { BYTE* pd = m_psurface->GetWritablePointer(); // // go forward // while (m_frameCurrent < newFrame) { PlayRLE( pd, m_prle + m_pdwOffsets[m_frameCurrent], m_prle + m_pdwOffsets[m_frameCurrent + 1] ); m_frameCurrent++; } // // go backward // while (m_frameCurrent > newFrame) { m_frameCurrent--; PlayRLE( pd, m_prle + m_pdwOffsets[m_frameCurrent], m_prle + m_pdwOffsets[m_frameCurrent + 1] ); } m_psurface->ReleasePointer(); } } ////////////////////////////////////////////////////////////////////////////// // // Image Methods // ////////////////////////////////////////////////////////////////////////////// TRef GetSurface() { UpdateFrame(); return m_psurface; } void Render(Context* pcontext) { UpdateFrame(); pcontext->DrawImage(m_psurface); } ////////////////////////////////////////////////////////////////////////////// // // Value Methods // ////////////////////////////////////////////////////////////////////////////// ZString GetFunctionName() { return "FrameImage"; } void Write(IMDLBinaryFile* pmdlFile) { WriteChildren(pmdlFile); pmdlFile->WriteReference("FrameImage"); TRef pfile = pmdlFile->WriteBinary(); pfile->Write((void*)&m_nFrame, sizeof(m_nFrame) ); pfile->Write((void*)m_pdwOffsets, sizeof(DWORD) * (m_nFrame)); pfile->Write((void*)m_prle, m_pdwOffsets[m_nFrame - 1]); pfile->WritePad(m_pdwOffsets[m_nFrame - 1]); } }; ////////////////////////////////////////////////////////////////////////////// // // Factory // ////////////////////////////////////////////////////////////////////////////// class FrameImageFactory : public IFunction { public: FrameImageFactory() { } TRef Apply(ObjectStack& stack) { ZUnimplemented(); return NULL; } TRef Read(IBinaryReaderSite* psite, ObjectStack& stack) { Number* pframe = Number::Cast((IObject*)stack.Pop()); Image* pimage = Image::Cast((Value*)(IObject*)stack.Pop()); DWORD nFrame = psite->GetDWORD(); DWORD* pdwOffsets = (DWORD*)psite->GetPointer(); BYTE* prle = (BYTE*)(pdwOffsets + nFrame); psite->MovePointer( sizeof(DWORD) * nFrame + pdwOffsets[nFrame - 1] ); return (Value*)new FrameImage( pimage, pframe, nFrame, pdwOffsets, prle, psite->GetMemoryObject() ); } }; TRef CreateFrameImageFactory() { return new FrameImageFactory(); } ////////////////////////////////////////////////////////////////////////////// // // Animation Compressor // ////////////////////////////////////////////////////////////////////////////// class CompressAnimClass { private: BYTE* m_pdata; int m_sizeData; WinPoint m_sizeFrame; TRef m_psurfaceFile; WinPoint m_sizeFile ; int m_xframes ; int m_yframes ; int m_nframes ; int m_xsize ; int m_ysize ; public: CompressAnimClass( Surface* psurfaceFile, int xframes, int yframes, TRef& psurfaceBackground, DWORD*& pdwOffsets, BYTE*& prleStart ) { m_xframes = xframes; m_yframes = yframes; m_nframes = m_xframes * m_yframes; m_psurfaceFile = psurfaceFile; m_sizeFile = m_psurfaceFile->GetSize(); m_xsize = m_sizeFile.X() / xframes; m_ysize = m_sizeFile.Y() / yframes; m_sizeFrame = WinPoint(m_xsize, m_ysize); psurfaceBackground = m_psurfaceFile->CreateCompatibleSurface(m_sizeFrame, SurfaceType2D()); GetFrame(psurfaceBackground, 0); TRef psurface0 = m_psurfaceFile->CreateCompatibleSurface(m_sizeFrame, SurfaceType2D()); TRef psurface1 = m_psurfaceFile->CreateCompatibleSurface(m_sizeFrame, SurfaceType2D()); int length = psurface0->GetPitch() * m_ysize; int size = (length + 2) * m_nframes; prleStart = new BYTE[size]; pdwOffsets = new DWORD[m_nframes]; pdwOffsets[0] = 0; BYTE* pd = new BYTE[length]; BYTE* prle = prleStart; for (int frame = 0; frame < m_nframes - 1; frame++) { GetFrame(psurface0, frame ); GetFrame(psurface1, frame + 1); // // Xor the two frames // const BYTE* p0 = psurface0->GetPointer(); const BYTE* p1 = psurface1->GetPointer(); DoXOR(p0, p1, pd, length); // // Calculate the difference // DoRLE(pd, length, prle); pdwOffsets[frame + 1 ] = prle - prleStart; } ZAssert(prle - prleStart <= size); delete pd; } void GetFrame(Surface* psurface, int frame) { int x = (frame % m_xframes) * m_xsize; int y = (frame / m_xframes) * m_ysize; psurface->BitBlt( WinPoint(0, 0), m_psurfaceFile, WinRect(x, y, x + m_xsize, y + m_ysize) ); } void DoXOR(const BYTE* p0, const BYTE* p1, BYTE* pd, int length) { for (int index = 0; index < length; index++) { pd[index] = p0[index] ^ p1[index]; } } template void DoStoreSpan(Type** ppd, BYTE*& prle, WORD code, WORD count) { *(WORD*)prle = code | count; *(Type*)(prle + 2) = (*ppd)[0]; (*ppd) += count; prle += sizeof(Type) + 2; } template WORD CouldStoreSpan(Type** ppd, Type* pend) { if ((*ppd) + 1 < pend) { Type value = (*ppd)[0]; WORD count = 0; while ( ((*ppd) + count + 1 < pend) && (*ppd)[count] == value ) { count++; } if (count * sizeof(Type) > 2 + sizeof(Type)) { return min(count, RLELengthMask); } } return 0; } void DoRawStoreSpan(BYTE* pstart, BYTE*& pd, BYTE*&prle) { WORD length = pd - pstart; if (length > 0) { *(WORD*)prle = RLEMaskFill | length; memcpy(prle + 2, pstart, length); prle += 2 + length; } } void DoRLE(BYTE* pd, int length, BYTE*& prle) { BYTE* pend = pd + length; BYTE* pstart = pd; while (pd < pend) { WORD count; if (count = CouldStoreSpan((DWORD**)&pd, (DWORD*)pend)) { DoRawStoreSpan(pstart, pd, prle); DoStoreSpan((DWORD**)&pd, prle, RLEMaskDWORD, count); pstart = pd; } else if (count = CouldStoreSpan((WORD**)&pd, (WORD*)pend)) { DoRawStoreSpan(pstart, pd, prle); DoStoreSpan((WORD**)&pd, prle, RLEMaskWORD, count); pstart = pd; } else if (count = CouldStoreSpan((BYTE**)&pd, (BYTE*)pend)) { DoRawStoreSpan(pstart, pd, prle); DoStoreSpan((BYTE**)&pd, prle, RLEMaskBYTE, count); pstart = pd; } pd++; } DoRawStoreSpan(pstart, pd, prle); } }; ////////////////////////////////////////////////////////////////////////////// // // Constructor // ////////////////////////////////////////////////////////////////////////////// TRef CreateFrameImage( Number* pframe, Surface* psurface, int xframes, int yframes ) { // // compress the animation // TRef psurfaceBackground; DWORD* pdwOffsets; BYTE* prle; CompressAnimClass foo( psurface, xframes, yframes, psurfaceBackground, pdwOffsets, prle ); // // Create a frame image // return new FrameImage( new ConstantImage(psurfaceBackground, ZString()), pframe, xframes * yframes, pdwOffsets, prle, NULL ); } ////////////////////////////////////////////////////////////////////////////// // // FrameImageButtonPane // ////////////////////////////////////////////////////////////////////////////// class FrameImageButtonPaneImpl : public FrameImageButtonPane, public Value { TRef m_pframe; TRef m_pimage; TRef m_peventSource; TRef m_peventMouseEnterSource; TRef m_peventMouseLeaveSource; bool m_bInside; float m_duration; float m_valueStart; float m_timeStart; float m_loop; float m_finish; public: FrameImageButtonPaneImpl( Number* ptime, Image* pimage, ModifiableNumber* pframe, float duration, bool loop, bool finish ) : Value(ptime), m_peventSource(new EventSourceImpl()), m_peventMouseEnterSource(new EventSourceImpl()), m_peventMouseLeaveSource(new EventSourceImpl()), m_pimage(pimage), m_pframe(pframe), m_duration(duration), m_loop(loop), m_finish(finish), m_bInside(false), m_valueStart(0), m_timeStart(ptime->GetValue()) { InternalSetSize( WinPoint::Cast(m_pimage->GetBounds().GetRect().Size()) ); } Number* GetTime() { return Number::Cast(GetChild(0)); } ////////////////////////////////////////////////////////////////////////////// // // FrameImageButtonPane methods // ////////////////////////////////////////////////////////////////////////////// IEventSource* GetEventSource() { return m_peventSource; } IEventSource* GetMouseEnterEventSource() { return m_peventMouseEnterSource; } IEventSource* GetMouseLeaveEventSource() { return m_peventMouseLeaveSource; } ////////////////////////////////////////////////////////////////////////////// // // Value Methods // ////////////////////////////////////////////////////////////////////////////// void ChildChanged(Value* pvalue, Value* pvalueNew) { Value::ChildChanged(pvalue, pvalueNew); NeedPaint(); } void Evaluate() { float time = GetTime()->GetValue(); float value; float delta = (time - m_timeStart) / m_duration; if (m_bInside) { value = m_valueStart + delta; if (m_loop) { // wrap the value back around to the beginning value = float(value - (int)value); } } else { if (m_finish) { value = m_valueStart + delta; if (value > 1.0) value = 0.0; } else if (m_loop) { value = m_valueStart; } else { value = m_valueStart - delta; } } value = bound(value, 0.0f, 1.0f); if (value != m_pframe->GetValue()) { m_pframe->SetValue(value); NeedPaint(); } } ////////////////////////////////////////////////////////////////////////////// // // Pane Methods // ////////////////////////////////////////////////////////////////////////////// void Paint(Surface* psurface) { Update(); psurface->BitBlt(WinPoint(0, 0), m_pimage->GetSurface()); } ////////////////////////////////////////////////////////////////////////////// // // IMouseInput Methods // ////////////////////////////////////////////////////////////////////////////// void MouseEnter(IInputProvider* pprovider, const Point& point) { m_bInside = true; m_valueStart = m_pframe->GetValue(); m_timeStart = GetTime()->GetValue(); m_peventMouseEnterSource->Trigger(); } void MouseLeave(IInputProvider* pprovider) { m_bInside = false; m_valueStart = m_pframe->GetValue(); m_timeStart = GetTime()->GetValue(); m_peventMouseLeaveSource->Trigger(); } MouseResult Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown) { if (button == 0) { if (bDown) { m_peventSource->Trigger(); } } return MouseResult(); } }; TRef CreateFrameImageButtonPane( Number* ptime, Image* pimage, ModifiableNumber* pframe, float duration, bool loop, bool finish ) { return new FrameImageButtonPaneImpl(ptime, pimage, pframe, duration, loop, finish); }