winamp/Src/Wasabi/api/wnd/wndclass/qpaintwnd.cpp
2024-09-24 14:54:57 +02:00

357 lines
7.3 KiB
C++

#include <precomp.h>
#include "qpaintwnd.h"
#include <tataki/canvas/bltcanvas.h>
#include "../nu/threadpool/TimerHandle.hpp"
#define TIMER_QUICKPAINT 0x650
// thread context, this is here so we can avoid the windows types in quickpaintwnd.h
class QuickPaintContext
{
public:
QuickPaintContext(QuickPaintWnd *_wnd, int timeout)
{
killswitch = 0;
wnd = _wnd;
death = CreateEvent(NULL, FALSE, FALSE, NULL);
timer_ms = timeout;
WASABI_API_THREADPOOL->AddHandle(0, timer_handle, QPThreadPoolFunc, (void *)this, 0, 0/*api_threadpool::FLAG_LONG_EXECUTION*/);
timer_handle.Wait(timer_ms);
}
~QuickPaintContext()
{
CloseHandle(death);
timer_handle.Close();
}
void Kill()
{
InterlockedExchangePointer((volatile PVOID*)&wnd, 0);
killswitch=1;
WaitForSingleObject(death, INFINITE);
}
QuickPaintWnd *wnd;
TimerHandle timer_handle;
HANDLE death;
volatile int killswitch;
int timer_ms;
static int QPThreadPoolFunc(HANDLE h, void *user_data, intptr_t t);
};
int QuickPaintContext::QPThreadPoolFunc(HANDLE h, void *user_data, intptr_t t)
{
QuickPaintContext *context = (QuickPaintContext *)user_data;
if (context->killswitch)
{
WASABI_API_THREADPOOL->RemoveHandle(0, h);
SetEvent(context->death);
}
else
{
DWORD start = GetTickCount();
QuickPaintWnd *wnd = 0;
InterlockedExchangePointer((volatile PVOID*)&wnd, context->wnd);
if (wnd)
{
wnd->quickPaint();
TimerHandle timer_handle(h);
DWORD end = GetTickCount();
if (end-start > (DWORD)context->timer_ms)
timer_handle.Wait(1);
else
timer_handle.Wait(context->timer_ms - (end - start));
}
}
return 0;
}
// -----------------------------------------------------------------------
QuickPaintWnd::QuickPaintWnd()
{
invalidates_required = 0;
realtime = 1;
canvas_w = -1;
canvas_h = -1;
timerset = 0;
speed = 25;
enabled = 0;
render_canvas1 = NULL;
render_canvas2 = NULL;
paint_canvas = NULL;
thread_context = 0;
while (1)
{
svc_skinFilter *obj = sfe.getNext();
if (!obj) break;
filters.addItem(obj);
}
invalidated = 0;
}
// -----------------------------------------------------------------------
QuickPaintWnd::~QuickPaintWnd()
{
foreach(filters)
sfe.release(filters.getfor());
endfor;
KillThread();
delete render_canvas1;
delete render_canvas2;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::setRealtime(int rt)
{
realtime = rt;
}
// -----------------------------------------------------------------------
int QuickPaintWnd::getRealtime() const
{
return realtime;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::setSpeed(int ms)
{
speed = ms;
if (enabled && timerset)
{
if (thread_context)
thread_context->timer_ms = ms;
// let it change the timer value on the invalidate() timer
killTimer(TIMER_QUICKPAINT);
setTimer(TIMER_QUICKPAINT, getSpeed());
}
}
// -----------------------------------------------------------------------
void QuickPaintWnd::startQuickPaint()
{
enabled = 1;
if (!isInited()) return;
CreateRenderThread();
timerset=1;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::stopQuickPaint()
{
enabled = 0;
if (!isInited()) return;
KillThread();
timerset=0;
}
// -----------------------------------------------------------------------
int QuickPaintWnd::isQuickPainting()
{
return enabled;
}
// -----------------------------------------------------------------------
int QuickPaintWnd::getSpeed()
{
return speed;
}
// -----------------------------------------------------------------------
int QuickPaintWnd::onInit()
{
QUICKPAINTWND_PARENT::onInit();
if (enabled)
{
ASSERT(!thread_context);
CreateRenderThread();
timerset = 1;
}
return 1;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::timerCallback(int id)
{
switch (id)
{
case TIMER_QUICKPAINT:
if (invalidates_required)
{
invalidated = 1;
if (getRealtime() && isVisible() && !isMinimized())
cascadeRepaint();
else
invalidate();
InterlockedExchange(&invalidates_required, 0);
}
//quickPaint();
break;
default:
QUICKPAINTWND_PARENT::timerCallback(id);
}
}
void QuickPaintWnd::SetPaintingCanvas(BltCanvas *c)
{
InterlockedExchangePointer((volatile PVOID*)&paint_canvas, c);
}
BltCanvas *&QuickPaintWnd::GetDrawingConvas()
{
if (paint_canvas == render_canvas2)
return render_canvas1;
else
return render_canvas2;
}
// -----------------------------------------------------------------------
int QuickPaintWnd::quickPaint()
{
int repaint=0;
int w, h;
getQuickPaintSize(&w, &h);
if (wantEvenAlignment())
{
if (w & 1) w++;
if (h & 1) h++;
}
if (w == 0 && h == 0) return 0;
BltCanvas *&render_canvas = GetDrawingConvas();
int newone = 0;
if (canvas_w != w || canvas_h != h)
{
delete render_canvas1; render_canvas1=0;
delete render_canvas2; render_canvas2=0;
}
if (!render_canvas)
{
render_canvas = new BltCanvas(w, wantNegativeHeight() ? -h : h, getOsWindowHandle());
canvas_w = w;
canvas_h = h;
newone = 1;
}
repaint = onQuickPaint(render_canvas, canvas_w, canvas_h, newone);
SetPaintingCanvas(render_canvas);
if (repaint)
InterlockedIncrement(&invalidates_required);
return repaint;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::getQuickPaintSize(int *w, int *h)
{
RECT r;
getClientRect(&r);
if (w) *w = r.right - r.left;
if (h) *h = r.bottom - r.top;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::getQuickPaintSource(RECT *r)
{
ASSERT(r != NULL);
r->left = 0;
r->right = canvas_w;
r->top = 0;
r->bottom = canvas_h;
}
// -----------------------------------------------------------------------
void QuickPaintWnd::getQuickPaintDest(RECT *r)
{
ASSERT(r != NULL);
getClientRect(r);
}
// -----------------------------------------------------------------------
void QuickPaintWnd::onSetVisible(int show)
{
QUICKPAINTWND_PARENT::onSetVisible(show);
if (!show)
{
if (timerset)
{
KillThread();
timerset = 0;
}
}
else
{
if (enabled && !timerset)
{
CreateRenderThread();
timerset = 1;
}
}
}
// -----------------------------------------------------------------------
int QuickPaintWnd::onPaint(Canvas *canvas)
{
QUICKPAINTWND_PARENT::onPaint(canvas);
if (!enabled) return 1;
BltCanvas *render_canvas;
InterlockedExchangePointer((volatile PVOID*)&render_canvas, paint_canvas);
if (!render_canvas) return 1;
RECT r;
getQuickPaintDest(&r);
RECT sr;
getQuickPaintSource(&sr);
if (invalidated && wantFilters())
{
foreach(filters)
filters.getfor()->filterBitmap((unsigned char *)render_canvas->getBits(), canvas_w, canvas_h, 32, NULL, getFiltersGroup());
endfor;
invalidated = 0;
}
render_canvas->/*getSkinBitmap()->*/stretchToRectAlpha(canvas, &sr, &r, getPaintingAlpha());
InterlockedExchange(&invalidates_required, 0);
return 1;
}
void QuickPaintWnd::KillThread()
{
if (thread_context)
{
killTimer(TIMER_QUICKPAINT);
thread_context->Kill();
delete thread_context;
thread_context = 0;
}
}
void QuickPaintWnd::CreateRenderThread()
{
int sp = getSpeed();
if (!thread_context)
{
thread_context = new QuickPaintContext(this, sp);
}
else
thread_context->timer_ms = sp;
setTimer(TIMER_QUICKPAINT, sp);
}