Files
Aerofoil/GpApp/AnimCursor.cpp

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?
// 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)
{
(*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);
}
}