//============================================================================ //---------------------------------------------------------------------------- // AnimCursor.c //---------------------------------------------------------------------------- //============================================================================ #include "Externs.h" #include "Environ.h" #include "IGpCursor.h" #include "IGpDisplayDriver.h" #include "MemoryManager.h" #include "ResourceManager.h" #include "PLBigEndian.h" #include "PLDrivers.h" #include "PLResources.h" #include "PLSysCalls.h" #include #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 acurHandle; typedef struct { struct { IGpCursor *hwCursor; } frame[1]; } compiledAcurRec; typedef THandle 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 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(cursorData); cursorData = cursorHeader + 1; cursorData = static_cast(reinterpret_cast(cursorDataBase) + cursorHeader->m_pixMapOffset); const CursorPixMapPrefix *cursorPixMapPrefix = static_cast(cursorData); cursorData = cursorPixMapPrefix + 1; const BEPixMap *pixMap = reinterpret_cast(reinterpret_cast(cursorData)); cursorData = pixMap + 1; cursorData = static_cast(reinterpret_cast(cursorDataBase) + cursorHeader->m_pixDataOffset); const uint8_t *pixMapDataBytes = static_cast(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(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(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(pixMapDataBytes + bytesRequired); const BEColorTableHeader *colorTableHeader = static_cast(cursorData); cursorData = colorTableHeader + 1; const BEColorTableItem *ctabItems = static_cast(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(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(pixelDataRGBA + i)[3] = 0; } mm->Release(colorValues); IGpCursor *cursor = PLDrivers::GetDisplayDriver()->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; PLDrivers::GetDisplayDriver()->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(); if (ballCursH && ballCursH != animCursorH) { compiledBallCursorH = NewHandle(sizeof(compiledAcurRec) * (*ballCursH)->n).StaticCast(); 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()); } //-------------------------------------------------------------- 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; PLDrivers::GetDisplayDriver()->SetCursor((*compiledAnimCursorH)->frame[(*animCursorH)->index].hwCursor); } else PLDrivers::GetDisplayDriver()->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; PLDrivers::GetDisplayDriver()->SetCursor((*compiledAnimCursorH)->frame[(*animCursorH)->index].hwCursor); } else PLDrivers::GetDisplayDriver()->SetStandardCursor(EGpStandardCursors::kWait); } //-------------------------------------------------------------- SpinCursor // Advances the beach ball cursor the number of frames specified. void SpinCursor (short incrementIndex) { PL_ASYNCIFY_PARANOID_DISARM_FOR_SCOPE(); 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); } }