mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 14:53:52 +00:00
404 lines
10 KiB
C++
404 lines
10 KiB
C++
|
|
//============================================================================
|
|
//----------------------------------------------------------------------------
|
|
// AnimCursor.c
|
|
//----------------------------------------------------------------------------
|
|
//============================================================================
|
|
|
|
|
|
#include "PLResources.h"
|
|
#include "PLBigEndian.h"
|
|
#include "Externs.h"
|
|
#include "Environ.h"
|
|
#include "HostDisplayDriver.h"
|
|
#include "IGpCursor.h"
|
|
#include "IGpDisplayDriver.h"
|
|
#include "MemoryManager.h"
|
|
#include "ResourceManager.h"
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
#define rAcurID 128
|
|
#define rHandCursorID 1000
|
|
|
|
|
|
typedef struct
|
|
{
|
|
BEInt16_t n;
|
|
BEInt16_t index;
|
|
struct
|
|
{
|
|
BEInt16_t resID;
|
|
BEInt16_t reserved;
|
|
} frame[1];
|
|
} acurRec;
|
|
|
|
typedef THandle<acurRec> acurHandle;
|
|
|
|
typedef struct
|
|
{
|
|
struct
|
|
{
|
|
IGpCursor *hwCursor;
|
|
} frame[1];
|
|
} compiledAcurRec;
|
|
|
|
typedef THandle<compiledAcurRec> compiledAcurHandle;
|
|
|
|
|
|
Boolean GetColorCursors (acurHandle, compiledAcurHandle);
|
|
void InitAnimatedCursor (acurHandle);
|
|
|
|
|
|
acurHandle animCursorH = nil;
|
|
compiledAcurHandle compiledAnimCursorH = nil;
|
|
|
|
|
|
//============================================================== Functions
|
|
|
|
//-------------------------------------------------------------- GetColorCursors
|
|
|
|
// Loads color cursors (for animated beach ball).
|
|
|
|
static uint8_t CompactChannel(const uint8_t *doublet)
|
|
{
|
|
unsigned int doubletValue = (doublet[0] << 8) + doublet[1];
|
|
|
|
return (doubletValue * 2 + 0x101) / 0x202;
|
|
}
|
|
|
|
IGpCursor *LoadColorCursor(int16_t resID)
|
|
{
|
|
const THandle<void> resHdl = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('crsr', resID);
|
|
if (!resHdl)
|
|
return nullptr;
|
|
|
|
// The color cursor format is not fully documented. Appears to be:
|
|
// Header
|
|
// CursorPixMapPrefix (at pixMapOffset)
|
|
// BEPixMap
|
|
// Pixel data
|
|
// BEColorTableHeader
|
|
// BEColorTableItem[...]
|
|
|
|
struct CursorPixMapPrefix
|
|
{
|
|
BEUInt32_t m_unknown; // Seems to always be zero
|
|
BEUInt16_t m_subFormat; // 0x8002 = 2 colors, 0x8004 = 4 colors, 0x8008 = 16 colors, 0x8010 = 256 colors
|
|
};
|
|
|
|
struct CursorHeader
|
|
{
|
|
BEUInt16_t m_cursorType;
|
|
BEUInt32_t m_pixMapOffset;
|
|
BEUInt32_t m_pixDataOffset;
|
|
BEUInt32_t m_expandedData;
|
|
BEUInt16_t m_expandedDataDepth;
|
|
BEUInt32_t m_unused;
|
|
uint8_t m_bwCursor[32];
|
|
uint8_t m_mask[32];
|
|
BEUInt16_t m_hotSpotY;
|
|
BEUInt16_t m_hotSpotX;
|
|
BEUInt32_t m_colorTableResourceID;
|
|
BEUInt32_t m_cursorResourceID;
|
|
};
|
|
|
|
const void *cursorDataBase = *resHdl;
|
|
const void *cursorData = cursorDataBase;
|
|
|
|
const CursorHeader *cursorHeader = static_cast<const CursorHeader *>(cursorData);
|
|
cursorData = cursorHeader + 1;
|
|
cursorData = static_cast<const void*>(reinterpret_cast<const uint8_t*>(cursorDataBase) + cursorHeader->m_pixMapOffset);
|
|
|
|
const CursorPixMapPrefix *cursorPixMapPrefix = static_cast<const CursorPixMapPrefix *>(cursorData);
|
|
|
|
cursorData = cursorPixMapPrefix + 1;
|
|
|
|
const BEPixMap *pixMap = reinterpret_cast<const BEPixMap*>(reinterpret_cast<const uint8_t*>(cursorData));
|
|
cursorData = pixMap + 1;
|
|
|
|
cursorData = static_cast<const void*>(reinterpret_cast<const uint8_t*>(cursorDataBase) + cursorHeader->m_pixDataOffset);
|
|
const uint8_t *pixMapDataBytes = static_cast<const uint8_t*>(cursorData);
|
|
|
|
const Rect rect = pixMap->m_bounds.ToRect();
|
|
const int width = rect.right - rect.left;
|
|
const int height = rect.bottom - rect.top;
|
|
const int componentSize = static_cast<int>(pixMap->m_componentSize);
|
|
|
|
switch (componentSize)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
const int bitsRequired = width * height * componentSize;
|
|
const int bytesRequired = (bitsRequired + 7) / 8;
|
|
|
|
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
|
|
|
|
uint8_t *colorValues = static_cast<uint8_t*>(mm->Alloc(width * height));
|
|
if (!colorValues)
|
|
{
|
|
resHdl.Dispose();
|
|
return nullptr;
|
|
}
|
|
|
|
const int numPixels = width * height;
|
|
|
|
switch (componentSize)
|
|
{
|
|
case 1:
|
|
for (int i = 0; i < numPixels; i++)
|
|
colorValues[i] = (pixMapDataBytes[i / 8] >> (7 - (i & 7) * 1)) & 0x1;
|
|
break;
|
|
case 2:
|
|
for (int i = 0; i < numPixels; i++)
|
|
colorValues[i] = (pixMapDataBytes[i / 4] >> (6 - (i & 3) * 2)) & 0x3;
|
|
break;
|
|
case 4:
|
|
for (int i = 0; i < numPixels; i++)
|
|
colorValues[i] = (pixMapDataBytes[i / 2] >> (4 - (i & 1) * 4)) & 0xf;
|
|
break;
|
|
case 8:
|
|
for (int i = 0; i < numPixels; i++)
|
|
colorValues[i] = pixMapDataBytes[i];
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
cursorData = static_cast<const void*>(pixMapDataBytes + bytesRequired);
|
|
const BEColorTableHeader *colorTableHeader = static_cast<const BEColorTableHeader *>(cursorData);
|
|
|
|
cursorData = colorTableHeader + 1;
|
|
|
|
const BEColorTableItem *ctabItems = static_cast<const BEColorTableItem *>(cursorData);
|
|
|
|
uint32_t decodedCTabItems[256];
|
|
const int numCTabItems = colorTableHeader->m_numItemsMinusOne + 1;
|
|
|
|
for (int i = 0; i < numCTabItems; i++)
|
|
{
|
|
const BEColorTableItem &item = ctabItems[i];
|
|
unsigned char rgba[4];
|
|
rgba[0] = CompactChannel(item.m_red);
|
|
rgba[1] = CompactChannel(item.m_green);
|
|
rgba[2] = CompactChannel(item.m_blue);
|
|
rgba[3] = 255;
|
|
|
|
int index = item.m_index;
|
|
assert(index >= 0 && index < 256);
|
|
|
|
memcpy(decodedCTabItems + index, rgba, 4);
|
|
}
|
|
|
|
uint32_t *pixelDataRGBA = static_cast<uint32_t*>(mm->Alloc(width * height * 4));
|
|
if (!pixelDataRGBA)
|
|
{
|
|
mm->Release(colorValues);
|
|
resHdl.Dispose();
|
|
return nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < numPixels; i++)
|
|
{
|
|
const uint8_t ctabIndex = colorValues[i];
|
|
pixelDataRGBA[i] = decodedCTabItems[ctabIndex];
|
|
|
|
if (((cursorHeader->m_mask[i / 8] >> (7 - (i & 7))) & 1) == 0)
|
|
reinterpret_cast<uint8_t*>(pixelDataRGBA + i)[3] = 0;
|
|
}
|
|
|
|
mm->Release(colorValues);
|
|
|
|
IGpCursor *cursor = PortabilityLayer::HostDisplayDriver::GetInstance()->CreateColorCursor(width, height, pixelDataRGBA, cursorHeader->m_hotSpotX, cursorHeader->m_hotSpotY);
|
|
|
|
mm->Release(pixelDataRGBA);
|
|
|
|
resHdl.Dispose();
|
|
|
|
return cursor;
|
|
}
|
|
|
|
Boolean GetColorCursors (acurHandle ballCursH, compiledAcurHandle compiledBallCursH)
|
|
{
|
|
short i, j;
|
|
IGpCursor *hwCursor;
|
|
Boolean result = true;
|
|
|
|
if (ballCursH)
|
|
{
|
|
j = (*ballCursH)->n; // Get the number of cursors
|
|
HideCursor(); // Hide the cursor
|
|
for (i = 0; i < j; i++) // Walk through the acur resource
|
|
{
|
|
hwCursor = LoadColorCursor((*ballCursH)->frame[i].resID); // Get the cursor
|
|
if (hwCursor == nil) // Make sure a real cursor was returned
|
|
{ // If not, trash all cursors loaded
|
|
for (j = 0; j < i; j++)
|
|
(*compiledBallCursH)->frame[j].hwCursor->Destroy();
|
|
result = false; // Tell calling proc we failed
|
|
break; // And break out of the loop
|
|
}
|
|
else // But, if the cursor loaded ok
|
|
{ // add it to our list or cursor handles
|
|
(*compiledBallCursH)->frame[i].hwCursor = hwCursor;
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->SetCursor(hwCursor);
|
|
}
|
|
}
|
|
InitCursor(); // Show the cursor again (as arrow)
|
|
}
|
|
return(result); // Return to calling proc w/ results
|
|
}
|
|
|
|
//-------------------------------------------------------------- InitAnimatedCursor
|
|
|
|
// Loads and sets up animated beach ball cursor structures.
|
|
|
|
void InitAnimatedCursor (acurHandle ballCursH)
|
|
{
|
|
if (thisMac.isTouchscreen)
|
|
return;
|
|
|
|
compiledAcurHandle compiledBallCursorH;
|
|
|
|
if (ballCursH == nil)
|
|
ballCursH = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('acur', 128).StaticCast<acurRec>();
|
|
if (ballCursH && ballCursH != animCursorH)
|
|
{
|
|
compiledBallCursorH = NewHandle(sizeof(compiledAcurRec) * (*ballCursH)->n).StaticCast<compiledAcurRec>();
|
|
if (!compiledBallCursorH)
|
|
RedAlert(kErrFailedResourceLoad);
|
|
|
|
DisposCursors();
|
|
|
|
GetColorCursors(ballCursH, compiledBallCursorH);
|
|
|
|
animCursorH = ballCursH;
|
|
compiledAnimCursorH = compiledBallCursorH;
|
|
(*ballCursH)->index = 0;
|
|
}
|
|
else
|
|
RedAlert(kErrFailedResourceLoad);
|
|
}
|
|
|
|
//-------------------------------------------------------------- LoadCursors
|
|
|
|
// Just calls the above function. Other code could be added here though?
|
|
// to add additional cursors.
|
|
|
|
void LoadCursors (void)
|
|
{
|
|
InitAnimatedCursor(PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('acur', rAcurID).StaticCast<acurRec>());
|
|
}
|
|
|
|
//-------------------------------------------------------------- DisposCursors
|
|
|
|
// Disposes of all memory allocated by anaimated beach ball cursors.
|
|
|
|
void DisposCursors (void)
|
|
{
|
|
short i, j;
|
|
|
|
if (compiledAnimCursorH != nil)
|
|
{
|
|
j = (*animCursorH)->n;
|
|
|
|
for (i = 0; i < j; i++)
|
|
{
|
|
if ((*compiledAnimCursorH)->frame[i].hwCursor != nil)
|
|
(*compiledAnimCursorH)->frame[i].hwCursor->Destroy();
|
|
}
|
|
|
|
compiledAnimCursorH.Dispose();
|
|
compiledAnimCursorH = nil;
|
|
}
|
|
|
|
if (animCursorH != nil)
|
|
{
|
|
animCursorH.Dispose();
|
|
animCursorH = nil;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- IncrementCursor
|
|
|
|
// Advances the beach ball cursor one frame.
|
|
|
|
void IncrementCursor (void)
|
|
{
|
|
if (animCursorH == 0)
|
|
InitAnimatedCursor(nil);
|
|
if (animCursorH)
|
|
{
|
|
acurRec *acur = *animCursorH;
|
|
acur->index++;
|
|
acur->index %= acur->n;
|
|
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->SetCursor((*compiledAnimCursorH)->frame[(*animCursorH)->index].hwCursor);
|
|
}
|
|
else
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->SetStandardCursor(EGpStandardCursors::kWait);
|
|
}
|
|
|
|
//-------------------------------------------------------------- DecrementCursor
|
|
|
|
// Reverses the beach ball cursor one frame.
|
|
|
|
void DecrementCursor (void)
|
|
{
|
|
if (animCursorH == 0)
|
|
InitAnimatedCursor(nil);
|
|
if (animCursorH)
|
|
{
|
|
(*animCursorH)->index--;
|
|
if (((*animCursorH)->index) < 0)
|
|
(*animCursorH)->index = ((*animCursorH)->n) - 1;
|
|
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->SetCursor((*compiledAnimCursorH)->frame[(*animCursorH)->index].hwCursor);
|
|
}
|
|
else
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->SetStandardCursor(EGpStandardCursors::kWait);
|
|
}
|
|
|
|
//-------------------------------------------------------------- SpinCursor
|
|
|
|
// Advances the beach ball cursor the number of frames specified.
|
|
|
|
void SpinCursor (short incrementIndex)
|
|
{
|
|
UInt32 dummyLong;
|
|
short i;
|
|
|
|
for (i = 0; i < incrementIndex; i++)
|
|
{
|
|
IncrementCursor();
|
|
Delay(1, &dummyLong);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- BackSpinCursor
|
|
|
|
// Reverses the beach ball cursor the number of frames specified.
|
|
|
|
void BackSpinCursor (short decrementIndex)
|
|
{
|
|
UInt32 dummyLong;
|
|
short i;
|
|
|
|
for (i = 0; i < decrementIndex; i++)
|
|
{
|
|
DecrementCursor();
|
|
Delay(1, &dummyLong);
|
|
}
|
|
}
|
|
|