mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 23:00:42 +00:00
399 lines
10 KiB
C++
399 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)
|
||
{
|
||
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);
|
||
|
||
GetColorCursors(ballCursH, compiledBallCursorH);
|
||
DisposCursors();
|
||
|
||
animCursorH = ballCursH;
|
||
compiledAnimCursorH = compiledBallCursorH;
|
||
(*ballCursH)->index = 0;
|
||
}
|
||
else
|
||
RedAlert(kErrFailedResourceLoad);
|
||
}
|
||
|
||
//-------------------------------------------------------------- LoadCursors
|
||
|
||
// Just calls the above function. Other code could be added here though<67>
|
||
// 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)
|
||
{
|
||
register 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)
|
||
{
|
||
(*animCursorH)->index++;
|
||
(*animCursorH)->index %= (*animCursorH)->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);
|
||
}
|
||
}
|
||
|