Path: blob/master/RSDKv5/RSDK/Graphics/Sprite.cpp
1163 views
#include "RSDK/Core/RetroEngine.hpp"12using namespace RSDK;34#if RETRO_REV0U5#include "Legacy/SpriteLegacy.cpp"6#endif78const int32 LOADING_IMAGE = 0;9const int32 LOAD_COMPLETE = 1;10const int32 LZ_MAX_CODE = 4095;11const int32 LZ_BITS = 12;12const int32 FIRST_CODE = 4097;13const int32 NO_SUCH_CODE = 4098;1415int32 codeMasks[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095 };1617int32 ReadGifCode(ImageGIF *image);18uint8 ReadGifByte(ImageGIF *image);19uint8 TraceGifPrefix(uint32 *prefix, int32 code, int32 clearCode);2021void InitGifDecoder(ImageGIF *image)22{23uint8 initCodeSize = ReadInt8(&image->info);24image->decoder->fileState = LOADING_IMAGE;25image->decoder->position = 0;26image->decoder->bufferSize = 0;27image->decoder->buffer[0] = 0;28image->decoder->depth = initCodeSize;29image->decoder->clearCode = 1 << initCodeSize;30image->decoder->eofCode = image->decoder->clearCode + 1;31image->decoder->runningCode = image->decoder->eofCode + 1;32image->decoder->runningBits = initCodeSize + 1;33image->decoder->maxCodePlusOne = 1 << image->decoder->runningBits;34image->decoder->stackPtr = 0;35image->decoder->prevCode = NO_SUCH_CODE;36image->decoder->shiftState = 0;37image->decoder->shiftData = 0;3839for (int32 i = 0; i <= LZ_MAX_CODE; ++i) image->decoder->prefix[i] = (uint8)NO_SUCH_CODE;40}41void ReadGifLine(ImageGIF *image, uint8 *line, int32 length, int32 offset)42{43int32 i = 0;44int32 stackPtr = image->decoder->stackPtr;45int32 eofCode = image->decoder->eofCode;46int32 clearCode = image->decoder->clearCode;47int32 prevCode = image->decoder->prevCode;4849if (stackPtr != 0) {50while (stackPtr != 0) {51if (i >= length)52break;5354line[offset++] = image->decoder->stack[--stackPtr];55i++;56}57}5859while (i < length) {60int32 gifCode = ReadGifCode(image);61if (gifCode == eofCode) {62if (i != length - 1 || image->decoder->pixelCount != 0)63return;6465i++;66}67else {68if (gifCode == clearCode) {69for (int32 p = 0; p <= LZ_MAX_CODE; p++) image->decoder->prefix[p] = NO_SUCH_CODE;7071image->decoder->runningCode = image->decoder->eofCode + 1;72image->decoder->runningBits = image->decoder->depth + 1;73image->decoder->maxCodePlusOne = 1 << image->decoder->runningBits;7475prevCode = image->decoder->prevCode = NO_SUCH_CODE;76}77else {78if (gifCode < clearCode) {79line[offset++] = (uint8)gifCode;80i++;81}82else {83if (gifCode < 0 || gifCode > LZ_MAX_CODE)84return;8586int32 code = gifCode;87if (image->decoder->prefix[gifCode] == NO_SUCH_CODE) {88if (gifCode != image->decoder->runningCode - 2)89return;9091code = prevCode;9293image->decoder->suffix[image->decoder->runningCode - 2] = image->decoder->stack[stackPtr++] =94TraceGifPrefix(image->decoder->prefix, prevCode, clearCode);95}9697int32 c = 0;98while (c++ <= LZ_MAX_CODE && code > clearCode && code <= LZ_MAX_CODE) {99image->decoder->stack[stackPtr++] = image->decoder->suffix[code];100code = image->decoder->prefix[code];101}102103if (c >= LZ_MAX_CODE || code > LZ_MAX_CODE)104return;105106image->decoder->stack[stackPtr++] = (uint8)code;107108while (stackPtr != 0 && i++ < length) line[offset++] = image->decoder->stack[--stackPtr];109}110111if (prevCode != NO_SUCH_CODE) {112if (image->decoder->runningCode < 2 || image->decoder->runningCode > FIRST_CODE)113return;114115image->decoder->prefix[image->decoder->runningCode - 2] = prevCode;116117if (gifCode == image->decoder->runningCode - 2)118image->decoder->suffix[image->decoder->runningCode - 2] = TraceGifPrefix(image->decoder->prefix, prevCode, clearCode);119else120image->decoder->suffix[image->decoder->runningCode - 2] = TraceGifPrefix(image->decoder->prefix, gifCode, clearCode);121}122123prevCode = gifCode;124}125}126}127128image->decoder->prevCode = prevCode;129image->decoder->stackPtr = stackPtr;130}131132int32 ReadGifCode(ImageGIF *image)133{134while (image->decoder->shiftState < image->decoder->runningBits) {135uint8 b = ReadGifByte(image);136image->decoder->shiftData |= (uint32)b << image->decoder->shiftState;137image->decoder->shiftState += 8;138}139140int32 result = (int32)(image->decoder->shiftData & (uint32)codeMasks[image->decoder->runningBits]);141image->decoder->shiftData >>= image->decoder->runningBits;142image->decoder->shiftState -= image->decoder->runningBits;143if (++image->decoder->runningCode > image->decoder->maxCodePlusOne && image->decoder->runningBits < LZ_BITS) {144image->decoder->maxCodePlusOne <<= 1;145image->decoder->runningBits++;146}147148return result;149}150151uint8 ReadGifByte(ImageGIF *image)152{153uint8 c = '\0';154if (image->decoder->fileState == LOAD_COMPLETE)155return c;156157uint8 b;158if (image->decoder->position == image->decoder->bufferSize) {159b = ReadInt8(&image->info);160image->decoder->bufferSize = b;161if (image->decoder->bufferSize == 0) {162image->decoder->fileState = LOAD_COMPLETE;163return c;164}165166ReadBytes(&image->info, image->decoder->buffer, image->decoder->bufferSize);167b = image->decoder->buffer[0];168image->decoder->position = 1;169}170else {171b = image->decoder->buffer[image->decoder->position++];172}173174return b;175}176177uint8 TraceGifPrefix(uint32 *prefix, int32 code, int32 clearCode)178{179int32 i = 0;180while (code > clearCode && i++ <= LZ_MAX_CODE) code = prefix[code];181182return code;183}184void ReadGifPictureData(ImageGIF *image, int32 width, int32 height, bool32 interlaced, uint8 *pixels)185{186int32 initialRows[] = { 0, 4, 2, 1 };187int32 rowInc[] = { 8, 8, 4, 2 };188189InitGifDecoder(image);190if (interlaced) {191for (int32 p = 0; p < 4; ++p) {192for (int32 y = initialRows[p]; y < height; y += rowInc[p]) {193ReadGifLine(image, pixels, width, y * width);194}195}196return;197}198for (int32 h = 0; h < height; ++h) ReadGifLine(image, pixels, width, h * width);199}200201bool32 ImageGIF::Load(const char *fileName, bool32 loadHeader)202{203if (!decoder)204return false;205206if (fileName) {207if (!LoadFile(&info, fileName, FMODE_RB))208return false;209210Seek_Set(&info, 6);211width = ReadInt16(&info);212height = ReadInt16(&info);213214if (loadHeader)215return true;216}217218int32 data = ReadInt8(&info);219// int32 has_pallete = (data & 0x80) >> 7;220// int32 colors = ((data & 0x70) >> 4) + 1;221int32 palette_size = (data & 0x7) + 1;222if (palette_size > 0)223palette_size = 1 << palette_size;224225Seek_Cur(&info, 2);226227if (!palette)228AllocateStorage((void **)&palette, 0x100 * sizeof(int32), DATASET_TMP, true);229230if (!pixels)231AllocateStorage((void **)&pixels, width * height, DATASET_TMP, false);232233if (palette && pixels) {234uint8 clr[3];235int32 c = 0;236do {237ReadBytes(&info, clr, 3);238palette[c] = (clr[0] << 16) | (clr[1] << 8) | (clr[2] << 0);239++c;240} while (c != palette_size);241242uint8 buf = ReadInt8(&info);243while (buf != ',') buf = ReadInt8(&info); // gif image start identifier244245ReadInt16(&info);246ReadInt16(&info);247ReadInt16(&info);248ReadInt16(&info);249data = ReadInt8(&info);250bool32 interlaced = (data & 0x40) >> 6;251if (data >> 7 == 1) {252int32 c = 0x80;253do {254++c;255ReadBytes(&info, clr, 3);256palette[c] = (clr[0] << 16) | (clr[1] << 8) | (clr[2] << 0);257} while (c != 0x100);258}259260ReadGifPictureData(this, width, height, interlaced, pixels);261262Close();263return true;264}265return false;266}267268#if RETRO_PLATFORM == RETRO_ANDROID269#define _REDOFF 0270#define _GREENOFF 8271#define _BLUEOFF 16272#else273#define _REDOFF 16274#define _GREENOFF 8275#define _BLUEOFF 0276#endif277278#if RETRO_REV02279void RSDK::ImagePNG::UnpackPixels_Greyscale(uint8 *pixelData)280{281uint8 *pixels = this->pixels;282for (int32 p = 0; p < this->width * this->height; ++p) {283uint8 brightness = *pixelData;284pixelData++;285286uint32 color = 0;287288// red channel289color = brightness << _REDOFF;290291// green channel292color |= brightness << _GREENOFF;293294// blue channel295color |= brightness << _BLUEOFF;296297// alpha channel298color |= brightness << 24;299300*pixels = color;301302pixels += 2;303}304}305306void RSDK::ImagePNG::UnpackPixels_GreyscaleA(uint8 *pixelData)307{308color *pixels = (color *)this->pixels;309for (int32 p = 0; p < this->width * this->height; ++p) {310uint8 brightness = *pixelData;311pixelData++;312313uint32 color = 0;314315// red channel316color = brightness << _REDOFF;317318// green channel319color |= brightness << _GREENOFF;320321// blue channel322color |= brightness << _BLUEOFF;323324// alpha channel325color |= 0xFF << 24;326327*pixels = color;328329pixels++;330}331}332333void RSDK::ImagePNG::UnpackPixels_Indexed(uint8 *pixelData)334{335color *pixels = (color *)this->pixels;336for (int32 p = 0; p < this->width * this->height; ++p) {337pixels[p] = palette[pixelData[p]] | 0xFF000000;338}339}340341void RSDK::ImagePNG::UnpackPixels_RGB(uint8 *pixelData)342{343color *pixels = (color *)this->pixels;344for (int32 p = 0; p < this->width * this->height; ++p) {345uint32 color = 0;346347// R348color = *pixelData << _REDOFF;349pixelData++;350351// G352color |= *pixelData << _GREENOFF;353pixelData++;354355// B356color |= *pixelData << _BLUEOFF;357pixelData++;358359// A360color |= 0xFF << 24;361362*pixels++ = color;363}364}365366void RSDK::ImagePNG::UnpackPixels_RGBA(uint8 *pixelData)367{368color *pixels = (color *)this->pixels;369for (int32 p = 0; p < this->width * this->height; ++p) {370uint32 color = 0;371372// R373color |= *pixelData << _REDOFF;374pixelData++;375376// G377color |= *pixelData << _GREENOFF;378pixelData++;379380// B381color |= *pixelData << _BLUEOFF;382pixelData++;383384// A385color |= *pixelData << 24;386pixelData++;387388*pixels++ = color;389}390}391392// from: https://raw.githubusercontent.com/lvandeve/lodepng/master/lodepng.cpp - paethPredictor()393uint8 paethPredictor(int16 a, int16 b, int16 c)394{395int16 pa = abs(b - c);396int16 pb = abs(a - c);397int16 pc = abs(a + b - c - c);398/* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */399if (pb < pa) {400a = b;401pa = pb;402}403404return (pc < pa) ? c : a;405}406407void RSDK::ImagePNG::Unfilter(uint8 *recon)408{409int32 bpp = (this->bitDepth + 7) >> 3;410switch (this->colorFormat) {411default: break;412413case PNGCLR_RGB: bpp *= sizeof(color) - 1; break;414415case PNGCLR_GREYSCALEA: bpp *= 2 * sizeof(uint8); break;416417case PNGCLR_RGBA: bpp *= sizeof(color); break;418}419420int32 pitch = bpp * this->width;421uint8 *scanline = recon;422423for (int32 y = 0; y < this->height; ++y) {424int32 filter = *scanline++;425426// prev scanline427uint8 *precon = y ? &recon[-pitch] : NULL;428429switch (filter) {430default:431#if !RETRO_USE_ORIGINAL_CODE432PrintLog(PRINT_NORMAL, "Invalid PNG Filter: %d", filter);433return;434#else435// [Fallthrough]436#endif437case PNGFILTER_NONE:438for (int32 c = 0; c < pitch; ++c) {439recon[c] = scanline[c];440}441break;442443case PNGFILTER_SUB:444for (int32 c = 0; c < bpp; ++c) {445recon[c] = scanline[c];446}447448for (int32 c = bpp, p = 0; c < pitch; ++c, ++p) {449recon[c] = scanline[c] + recon[p];450}451break;452453case PNGFILTER_UP:454if (precon) {455for (int32 c = 0; c < pitch; ++c) {456recon[c] = precon[c] + scanline[c];457}458}459else {460for (int32 c = 0; c < pitch; ++c) {461recon[c] = scanline[c];462}463}464break;465466case PNGFILTER_AVG:467if (precon) {468for (int32 c = 0; c < bpp; ++c) {469recon[c] = scanline[c] + (precon[c] >> 1);470}471472for (int32 c = bpp, p = 0; c < pitch; ++c, ++p) {473recon[c] = scanline[c] + ((recon[p] + precon[c]) >> 1);474}475}476else {477for (int32 c = 0; c < bpp; ++c) {478recon[c] = scanline[c];479}480481for (int32 c = bpp, p = 0; c < pitch; ++c, ++p) {482recon[c] = scanline[c] + (recon[p] >> 1);483}484}485break;486487case PNGFILTER_PAETH:488if (precon) {489for (int32 c = 0; c < bpp; ++c) {490recon[c] = (scanline[c] + precon[c]);491}492493for (int32 c = bpp, p = 0; c < pitch; ++c, ++p) {494recon[c] = (scanline[c] + paethPredictor(recon[c - bpp], precon[c], precon[p]));495}496}497else {498for (int32 c = 0; c < bpp; ++c) {499recon[c] = scanline[c];500}501502for (int32 c = bpp, p = 0; c < pitch; ++c, ++p) {503recon[c] = scanline[c] + recon[p];504}505}506break;507}508509recon += pitch;510scanline += pitch;511}512}513514bool32 RSDK::ImagePNG::AllocatePixels()515{516dataSize = sizeof(color) * height * (width + 1);517if (!pixels) {518AllocateStorage((void **)&pixels, dataSize, DATASET_TMP, false);519520if (!pixels) {521Close();522return false;523}524}525526return true;527}528529void RSDK::ImagePNG::ProcessScanlines()530{531uint8 *pixelsPtr = NULL;532switch (colorFormat) {533case PNGCLR_GREYSCALE:534case PNGCLR_INDEXED: pixelsPtr = &pixels[3 * width * height]; break;535536case PNGCLR_RGB: pixelsPtr = &pixels[1 * width * height]; break;537538case PNGCLR_GREYSCALEA: pixelsPtr = &pixels[2 * width * height]; break;539540case PNGCLR_RGBA:541default: pixelsPtr = &pixels[0 * width * height]; break;542}543544// the original v5 decodes IDAT sections one at a time, so the compressed buffer is extracted into pixels every time here545// again, this is a BAD idea!! IDAT chunks should be stored and processed as a group at the end of reading546547// decode all loaded IDAT chunks into pixels548Uncompress((uint8 **)&chunkBuffer, chunkSize, (uint8 **)&pixelsPtr, dataSize);549550Unfilter(pixelsPtr);551552switch (colorFormat) {553case PNGCLR_GREYSCALE: UnpackPixels_Greyscale(pixelsPtr); break;554555case PNGCLR_RGB: UnpackPixels_RGB(pixelsPtr); break;556557case PNGCLR_INDEXED: UnpackPixels_Indexed(pixelsPtr); break;558559case PNGCLR_GREYSCALEA: UnpackPixels_GreyscaleA(pixelsPtr); break;560561case PNGCLR_RGBA: UnpackPixels_RGBA(pixelsPtr); break;562563default: break;564}565}566567// PNG format signature568#define PNG_SIGNATURE 0xA1A0A0D474E5089LL // PNG (and other bytes I don't care about)569570// PNG chunk header signatures571#define PNG_SIG_HEADER 0x52444849 // IHDR572#define PNG_SIG_END 0x444E4549 // IEND573#define PNG_SIG_PALETTE 0x45544C50 // PLTE574#define PNG_SIG_DATA 0x54414449 // IDAT575576bool32 RSDK::ImagePNG::Load(const char *fileName, bool32 loadHeader)577{578if (fileName) {579if (LoadFile(&info, fileName, FMODE_RB)) {580if (ReadInt64(&info) == PNG_SIGNATURE) {581while (true) {582chunkSize = ReadInt32(&info, true);583chunkHeader = ReadInt32(&info, false);584585bool32 finished = false;586if (chunkHeader == PNG_SIG_HEADER && chunkSize == 13) {587width = ReadInt32(&info, true);588height = ReadInt32(&info, true);589bitDepth = ReadInt8(&info);590colorFormat = ReadInt8(&info);591compression = ReadInt8(&info);592filter = ReadInt8(&info);593interlaced = ReadInt8(&info);594if (interlaced || bitDepth != 8) {595Close();596return false;597}598depth = 32;599600#if !RETRO_USE_ORIGINAL_CODE601// image size should be enough space to hold all the IDAT chunks602AllocateStorage((void **)&chunkBuffer, sizeof(color) * height * (width + 1), DATASET_TMP, true);603dataSize = 0;604#endif605606if (loadHeader)607return true;608}609else if (chunkHeader == PNG_SIG_END) {610finished = true;611}612else if (chunkHeader == PNG_SIG_PALETTE) {613int32 colorCnt = chunkSize / 3;614if (!(chunkSize % 3)) {615chunkSize = colorCnt;616if (colorCnt <= 0x100) {617if (!palette)618AllocateStorage((void **)&palette, sizeof(uint32) * colorCnt, DATASET_TMP, true);619620uint8 channels[3];621for (int32 c = 0; c < colorCnt; ++c) {622ReadBytes(&info, channels, 3 * sizeof(uint8));623palette[c] = (channels[0] << 16) | (channels[1] << 8) | (channels[2] << 0);624}625}626}627}628else if (chunkHeader == PNG_SIG_DATA) {629#if RETRO_USE_ORIGINAL_CODE630if (!AllocatePixels())631return false;632633// read this chunk into the chunk buffer storage (we're processing each IDAT section by itself634// this is a BAD idea!!! though it's kept here for reference as to how the original v5 works635AllocateStorage((void **)&chunkBuffer, chunkSize, DATASET_TMP, false);636ReadBytes(&info, chunkBuffer, chunkSize);637638// decode the scanlines into usable RGBA pixels639ProcessScanlines();640#else641// read this chunk into the chunk buffer storage (we process em all at the end)642dataSize += ReadBytes(&info, chunkBuffer + dataSize, chunkSize);643#endif644}645else {646Seek_Cur(&info, chunkSize);647}648649chunkCRC = ReadInt32(&info, false);650651if (finished) {652Close();653654#if !RETRO_USE_ORIGINAL_CODE655// copy this over, since we only "borrowed" it after all :)656// chunkSize is the size of chunkBuffer657chunkSize = dataSize;658if (!AllocatePixels())659return false;660661// decode the scanlines into usable RGBA pixels662ProcessScanlines();663664RemoveStorageEntry((void **)&chunkBuffer);665#endif666667return true;668}669}670}671else {672Close();673}674}675}676677return false;678}679#endif680681#if !RETRO_REV02682bool32 RSDK::ImageTGA::Load(const char *fileName, bool32 loadHeader)683{684if (LoadFile(&info, fileName, FMODE_RB)) {685// header686uint8 idLength = ReadInt8(&info);687688// color map type689uint8 colormaptype = ReadInt8(&info);690691// image type692uint8 datatypecode = ReadInt8(&info);693694// color map specification695int16 colormaporigin = ReadInt16(&info);696int16 colormaplength = ReadInt16(&info);697uint8 colormapdepth = ReadInt8(&info);698699// image specification700int16 originX = ReadInt16(&info);701int16 originY = ReadInt16(&info);702width = ReadInt16(&info);703height = ReadInt16(&info);704uint8 bpp = ReadInt8(&info);705uint8 descriptor = ReadInt8(&info);706707bool32 reverse = (~descriptor >> 4) & 1;708if (bpp >= 16) {709if (idLength)710Seek_Cur(&info, idLength);711712AllocateStorage((void **)&pixels, sizeof(uint32) * height * width, DATASET_TMP, false);713uint32 *pixelsPtr = (uint32 *)pixels;714if (reverse)715pixelsPtr += (height * width) - width;716717int32 x = 0;718switch (datatypecode) {719case 2: // Uncompressed, RGB images720switch (bpp) {721case 16:722for (int32 i = 0; i < height * width; ++i) {723uint8 channels[2];724ReadBytes(&info, channels, sizeof(uint16));725726uint16 color16 = channels[0] + (channels[1] << 8);727*pixelsPtr = 0;728729if (color16 & 0x8000) { // alpha bit (0 = invisible, 1 = visible)730uint32 R = (color16 >> 10) & 0x1F;731uint32 G = (color16 >> 5) & 0x1F;732uint32 B = (color16 >> 0) & 0x1F;733734R = (R << 3) | (R >> 2);735G = (G << 3) | (G >> 2);736B = (B << 3) | (B >> 2);737738*pixelsPtr = (R << 16) | (G << 8) | (B << 0);739}740741pixelsPtr++;742743if (reverse && ++x == width) {744x = 0;745pixelsPtr -= width << 1;746}747}748break;749750case 24:751for (int32 i = 0; i < height * width; ++i) {752uint8 channels[3];753ReadBytes(&info, channels, sizeof(color) - 1);754755*pixelsPtr = (channels[0] << 0) | (channels[1] << 8) | (channels[2] << 16) | (0xFF << 24);756pixelsPtr++;757758if (reverse && ++x == width) {759x = 0;760pixelsPtr -= width << 1;761}762}763break;764765case 32:766for (int32 i = 0; i < height * width; ++i) {767uint8 channels[4];768ReadBytes(&info, channels, sizeof(color));769770*pixelsPtr = (channels[0] << 0) | (channels[1] << 8) | (channels[2] << 16) | (channels[3] << 24);771pixelsPtr++;772773if (reverse && ++x == width) {774x = 0;775pixelsPtr -= width << 1;776}777}778break;779}780break;781782case 10: // Runlength encoded RGB images783switch (bpp) {784case 16: {785uint8 channels[2];786memset(channels, 0, sizeof(channels));787788uint8 count = 0;789bool32 decodingRLE = false;790for (int32 p = 0; p < height * width; ++p) {791if (count) {792if (!decodingRLE)793ReadBytes(&info, channels, sizeof(uint16));794795--count;796}797else {798count = ReadInt8(&info);799decodingRLE = count & 0x80;800count &= 0x7F;801802ReadBytes(&info, channels, sizeof(uint16));803}804805uint16 color16 = channels[0] + (channels[1] << 8);806*pixelsPtr = 0;807808if (color16 & 0x8000) { // alpha bit (0 = invisible, 1 = visible)809uint32 R = (color16 >> 10) & 0x1F;810uint32 G = (color16 >> 5) & 0x1F;811uint32 B = (color16 >> 0) & 0x1F;812813R = (R << 3) | (R >> 2);814G = (G << 3) | (G >> 2);815B = (B << 3) | (B >> 2);816817*pixelsPtr = (R << 16) | (G << 8) | (B << 0);818}819820++pixelsPtr;821if (reverse && ++x == width) {822x = 0;823pixelsPtr -= width << 1;824}825}826break;827}828829case 24: {830uint8 channels[3];831memset(channels, 0, sizeof(channels));832833uint8 count = 0;834bool32 decodingRLE = false;835for (int32 p = 0; p < height * width; ++p) {836if (count) {837if (!decodingRLE)838ReadBytes(&info, channels, sizeof(color) - 1);839840--count;841}842else {843count = ReadInt8(&info);844decodingRLE = count & 0x80;845count &= 0x7F;846847ReadBytes(&info, channels, sizeof(color) - 1);848}849850*pixelsPtr = (channels[0] << 0) | (channels[1] << 8) | (channels[2] << 16) | (0xFF << 24);851pixelsPtr++;852853if (reverse && ++x == width) {854x = 0;855pixelsPtr -= width << 1;856}857}858break;859}860861case 32: {862uint8 channels[sizeof(color)];863memset(channels, 0, sizeof(channels));864865uint8 count = 0;866bool32 decodingRLE = false;867for (int32 p = 0; p < height * width; ++p) {868if (count) {869if (!decodingRLE)870ReadBytes(&info, channels, sizeof(uint32));871872--count;873}874else {875count = ReadInt8(&info);876decodingRLE = count & 0x80;877count &= 0x7F;878879ReadBytes(&info, channels, sizeof(color));880}881882*pixelsPtr = (channels[0] << 0) | (channels[1] << 8) | (channels[2] << 16) | (channels[3] << 24);883pixelsPtr++;884885if (reverse && ++x == width) {886x = 0;887pixelsPtr -= width << 1;888}889}890break;891}892}893break;894}895896Close();897return true;898}899}900901return false;902}903#endif904905uint16 RSDK::LoadSpriteSheet(const char *filename, uint8 scope)906{907char fullFilePath[0x100];908sprintf_s(fullFilePath, sizeof(fullFilePath), "Data/Sprites/%s", filename);909910if (!scope || scope > SCOPE_STAGE)911return -1;912913RETRO_HASH_MD5(hash);914GEN_HASH_MD5(filename, hash);915916for (int32 i = 0; i < SURFACE_COUNT; ++i) {917if (HASH_MATCH_MD5(gfxSurface[i].hash, hash)) {918return i;919}920}921922uint16 id = -1;923for (id = 0; id < SURFACE_COUNT; ++id) {924if (gfxSurface[id].scope == SCOPE_NONE)925break;926}927928if (id >= SURFACE_COUNT)929return -1;930931932GFXSurface *surface = &gfxSurface[id];933ImageGIF image;934935if (image.Load(fullFilePath, true)) {936surface->scope = scope;937surface->width = image.width;938surface->height = image.height;939surface->lineSize = 0;940memcpy(surface->hash, hash, 4 * sizeof(int32));941942int32 w = surface->width;943if (w > 1) {944int32 ls = 0;945do {946w >>= 1;947++ls;948} while (w > 1);949surface->lineSize = ls;950}951952surface->pixels = NULL;953AllocateStorage((void **)&surface->pixels, surface->width * surface->height, DATASET_STG, false);954#if !RETRO_USE_ORIGINAL_CODE955// Bug details: On a failed allocation, image.pixels will end up being reallocated in image.Load().956// Pixel data would then be loaded in this temporary buffer, but surface->pixels would never point to the actual data.957// This issue would only happen on cases where the STG mempool is full, such as ports with lower storage limits958// or with mods that use a lot of spritesheets.959// As a last resort, let's try a new allocation in TMP for surface->pixels.960// NOTE: This is a workaround, and will still cause a crash if the TMP allocation fails as well.961if (!surface->pixels)962AllocateStorage((void **)&surface->pixels, surface->width * surface->height, DATASET_TMP, false);963#endif964image.pixels = surface->pixels;965image.Load(NULL, false);966967#if RETRO_USE_ORIGINAL_CODE968image.palette = NULL;969image.decoder = NULL;970#endif971image.Close();972973return id;974}975else {976#if RETRO_USE_ORIGINAL_CODE977image.palette = NULL;978image.decoder = NULL;979#endif980image.pixels = NULL;981image.Close();982return -1;983}984}985986bool32 RSDK::LoadImage(const char *filename, double displayLength, double fadeSpeed, bool32 (*skipCallback)())987{988char fullFilePath[0x100];989sprintf_s(fullFilePath, sizeof(fullFilePath), "Data/Images/%s", filename);990991#if RETRO_REV02992ImagePNG image;993#else994ImageTGA image;995#endif996InitFileInfo(&image.info);997998#if RETRO_REV02999if (image.Load(fullFilePath, false)) {1000if (image.width == RETRO_VIDEO_TEXTURE_W && image.height == RETRO_VIDEO_TEXTURE_H) {1001RenderDevice::SetupImageTexture(image.width, image.height, image.pixels);1002}1003#if !RETRO_USE_ORIGINAL_CODE1004else {1005PrintLog(PRINT_NORMAL, "ERROR: Images must be 1024x512!");1006}1007#endif10081009engine.displayTime = displayLength;1010engine.storedShaderID = videoSettings.shaderID;1011engine.storedState = sceneInfo.state;1012videoSettings.dimMax = 0.0;1013videoSettings.shaderID = SHADER_RGB_IMAGE;1014videoSettings.screenCount = 0; // "Image Display Mode"1015engine.skipCallback = skipCallback;1016sceneInfo.state = ENGINESTATE_SHOWIMAGE;1017engine.imageFadeSpeed = fadeSpeed / 60.0;10181019image.pixels = NULL;1020image.Close();1021return true;1022}1023#elif !RETRO_REV021024if (image.Load(fullFilePath, true)) {1025if (image.width == RETRO_VIDEO_TEXTURE_W && image.height == RETRO_VIDEO_TEXTURE_H) {1026RenderDevice::SetupImageTexture(image.width, image.height, image.pixels);1027}1028#if !RETRO_USE_ORIGINAL_CODE1029else {1030PrintLog(PRINT_NORMAL, "ERROR: Images must be 1024x512!");1031}1032#endif10331034engine.displayTime = displayLength;1035engine.storedShaderID = videoSettings.shaderID;1036engine.storedState = sceneInfo.state;1037videoSettings.dimMax = 0.0;1038videoSettings.shaderID = SHADER_RGB_IMAGE;1039videoSettings.screenCount = 0; // "Image Display Mode"1040engine.skipCallback = skipCallback;1041sceneInfo.state = ENGINESTATE_SHOWIMAGE;1042engine.imageFadeSpeed = fadeSpeed / 60.0;10431044image.pixels = NULL;1045image.Close();1046return true;1047}1048#endif1049else {1050image.pixels = NULL;1051image.Close();1052}1053return false;1054}105510561057