mirror of
https://github.com/libretro/Mesen.git
synced 2024-12-13 11:57:09 +00:00
288 lines
9.1 KiB
C++
288 lines
9.1 KiB
C++
#include "stdafx.h"
|
|
#include <algorithm>
|
|
#include <unordered_map>
|
|
#include "HdNesPack.h"
|
|
#include "Console.h"
|
|
#include "MessageManager.h"
|
|
#include "EmulationSettings.h"
|
|
#include "HdPackLoader.h"
|
|
#include "../Utilities/FolderUtilities.h"
|
|
#include "../Utilities/PNGHelper.h"
|
|
|
|
HdNesPack::HdNesPack()
|
|
{
|
|
}
|
|
|
|
HdNesPack::~HdNesPack()
|
|
{
|
|
}
|
|
|
|
void HdNesPack::BlendColors(uint8_t output[4], uint8_t input[4])
|
|
{
|
|
uint8_t alpha = input[3] + 1;
|
|
uint8_t invertedAlpha = 256 - input[3];
|
|
output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8);
|
|
output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8);
|
|
output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8);
|
|
output[3] = 0xFF;
|
|
}
|
|
|
|
uint32_t HdNesPack::AdjustBrightness(uint8_t input[4], uint16_t brightness)
|
|
{
|
|
uint8_t output[4];
|
|
output[0] = std::min(255, (brightness * input[0]) >> 8);
|
|
output[1] = std::min(255, (brightness * input[1]) >> 8);
|
|
output[2] = std::min(255, (brightness * input[2]) >> 8);
|
|
output[3] = input[3];
|
|
return *((uint32_t*)output);
|
|
}
|
|
|
|
void HdNesPack::DrawColor(uint32_t color, uint32_t *outputBuffer, uint32_t scale, uint32_t screenWidth)
|
|
{
|
|
if(scale == 1) {
|
|
*outputBuffer = color;
|
|
} else {
|
|
for(uint32_t i = 0; i < scale; i++) {
|
|
std::fill(outputBuffer, outputBuffer + scale, color);
|
|
outputBuffer += screenWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HdNesPack::DrawCustomBackground(uint32_t *outputBuffer, uint32_t x, uint32_t y, uint32_t scale, uint32_t screenWidth)
|
|
{
|
|
if(scale == 1) {
|
|
*outputBuffer = GetCustomBackgroundPixel(x, y, 0, 0);
|
|
} else {
|
|
for(uint32_t i = 0; i < scale; i++) {
|
|
for(uint32_t j = 0; j < scale; j++) {
|
|
*outputBuffer = GetCustomBackgroundPixel(x, y, j, i);
|
|
outputBuffer++;
|
|
}
|
|
outputBuffer += screenWidth - scale;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HdNesPack::DrawTile(HdPpuTileInfo &tileInfo, HdPackTileInfo &hdPackTileInfo, uint32_t *outputBuffer, uint32_t screenWidth)
|
|
{
|
|
if(hdPackTileInfo.IsFullyTransparent) {
|
|
return;
|
|
}
|
|
|
|
uint32_t scale = GetScale();
|
|
uint32_t *bitmapData = hdPackTileInfo.HdTileData.data();
|
|
uint32_t tileWidth = 8 * scale;
|
|
uint8_t tileOffsetX = tileInfo.HorizontalMirroring ? 7 - tileInfo.OffsetX : tileInfo.OffsetX;
|
|
uint32_t bitmapOffset = (tileInfo.OffsetY * scale) * tileWidth + tileOffsetX * scale;
|
|
int32_t bitmapSmallInc = 1;
|
|
int32_t bitmapLargeInc = tileWidth - scale;
|
|
if(tileInfo.HorizontalMirroring) {
|
|
bitmapOffset += scale - 1;
|
|
bitmapSmallInc = -1;
|
|
bitmapLargeInc = tileWidth + scale;
|
|
}
|
|
if(tileInfo.VerticalMirroring) {
|
|
bitmapOffset += tileWidth * (scale - 1);
|
|
bitmapLargeInc = (tileInfo.HorizontalMirroring ? (int32_t)scale : -(int32_t)scale) - (int32_t)tileWidth;
|
|
}
|
|
|
|
uint32_t rgbValue;
|
|
if(hdPackTileInfo.HasTransparentPixels || hdPackTileInfo.Brightness < 255) {
|
|
for(uint32_t y = 0; y < scale; y++) {
|
|
for(uint32_t x = 0; x < scale; x++) {
|
|
if(hdPackTileInfo.Brightness == 255) {
|
|
rgbValue = *(bitmapData + bitmapOffset);
|
|
} else {
|
|
rgbValue = AdjustBrightness((uint8_t*)(bitmapData + bitmapOffset), hdPackTileInfo.Brightness);
|
|
}
|
|
|
|
if(!hdPackTileInfo.HasTransparentPixels || (bitmapData[bitmapOffset] & 0xFF000000) == 0xFF000000) {
|
|
*outputBuffer = rgbValue;
|
|
} else {
|
|
if(bitmapData[bitmapOffset] & 0xFF000000) {
|
|
BlendColors((uint8_t*)outputBuffer, (uint8_t*)&rgbValue);
|
|
}
|
|
}
|
|
outputBuffer++;
|
|
bitmapOffset += bitmapSmallInc;
|
|
}
|
|
bitmapOffset += bitmapLargeInc;
|
|
outputBuffer += screenWidth - scale;
|
|
}
|
|
} else {
|
|
for(uint32_t y = 0; y < scale; y++) {
|
|
for(uint32_t x = 0; x < scale; x++) {
|
|
*outputBuffer = *(bitmapData + bitmapOffset);
|
|
outputBuffer++;
|
|
bitmapOffset += bitmapSmallInc;
|
|
}
|
|
bitmapOffset += bitmapLargeInc;
|
|
outputBuffer += screenWidth - scale;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t HdNesPack::GetScale()
|
|
{
|
|
return Console::GetHdData()->Scale;
|
|
}
|
|
|
|
void HdNesPack::OnBeforeApplyFilter(HdPpuPixelInfo *screenTiles)
|
|
{
|
|
HdPackData* hdData = Console::GetHdData();
|
|
|
|
_palette = hdData->Palette.size() == 0x40 ? hdData->Palette.data() : EmulationSettings::GetRgbPalette();
|
|
|
|
if(hdData->OptionFlags & (int)HdPackOptions::NoSpriteLimit) {
|
|
EmulationSettings::SetFlags(EmulationFlags::RemoveSpriteLimit | EmulationFlags::AdaptiveSpriteLimit);
|
|
}
|
|
|
|
_backgroundIndex = -1;
|
|
for(size_t i = 0; i < hdData->Backgrounds.size(); i++) {
|
|
bool isMatch = true;
|
|
for(HdPackCondition* condition : hdData->Backgrounds[i].Conditions) {
|
|
if(!condition->CheckCondition(screenTiles, 0, 0, nullptr)) {
|
|
isMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(isMatch) {
|
|
_backgroundIndex = (int32_t)i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
HdPackTileInfo * HdNesPack::GetMatchingTile(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y, HdPpuTileInfo* tile)
|
|
{
|
|
HdPackData *hdData = Console::GetHdData();
|
|
auto hdTile = hdData->TileByKey.find(*tile);
|
|
if(hdTile == hdData->TileByKey.end()) {
|
|
hdTile = hdData->TileByKey.find(tile->GetKey(true));
|
|
}
|
|
|
|
if(hdTile != hdData->TileByKey.end()) {
|
|
for(HdPackTileInfo* hdPackTile : hdTile->second) {
|
|
if(hdPackTile->MatchesCondition(screenTiles, x, y, tile)) {
|
|
return hdPackTile;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool HdNesPack::IsNextToSprite(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y)
|
|
{
|
|
bool hasNonBackgroundSurrounding = false;
|
|
auto processAdjacentTile = [&hasNonBackgroundSurrounding](HdPpuPixelInfo& pixelInfo) {
|
|
if(pixelInfo.Tile.BgColorIndex != 0) {
|
|
hasNonBackgroundSurrounding = true;
|
|
} else {
|
|
for(int i = 0; i < pixelInfo.SpriteCount; i++) {
|
|
if(pixelInfo.Sprite[i].SpriteColorIndex == 0 || pixelInfo.Sprite[i].SpriteColor != pixelInfo.Sprite[i].BgColor) {
|
|
hasNonBackgroundSurrounding |= pixelInfo.Sprite[i].TileIndex != HdPpuTileInfo::NoTile && pixelInfo.Sprite[i].SpriteColorIndex != 0;
|
|
}
|
|
if(hasNonBackgroundSurrounding) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
for(int i = -1; i <= 1; i++) {
|
|
if((int)y + i < 0 || y + i >= PPU::ScreenHeight) {
|
|
continue;
|
|
}
|
|
|
|
for(int j = -1; j <= 1; j++) {
|
|
if((int)x + j < 0 || x + j >= PPU::ScreenWidth) {
|
|
continue;
|
|
}
|
|
|
|
if(!hasNonBackgroundSurrounding) {
|
|
processAdjacentTile(screenTiles[(i + y) * 256 + j + x]);
|
|
}
|
|
}
|
|
}
|
|
return hasNonBackgroundSurrounding;
|
|
}
|
|
|
|
uint32_t HdNesPack::GetCustomBackgroundPixel(int x, int y, int offsetX, int offsetY)
|
|
{
|
|
HdPackData *hdData = Console::GetHdData();
|
|
uint8_t brightness = hdData->Backgrounds[_backgroundIndex].Brightness;
|
|
uint32_t width = hdData->Backgrounds[_backgroundIndex].Data->Width;
|
|
uint32_t rgbColor = *(hdData->Backgrounds[_backgroundIndex].data() + (y * hdData->Scale + offsetY) * width + x * hdData->Scale + offsetX);
|
|
if(brightness < 255) {
|
|
return AdjustBrightness((uint8_t*)&rgbColor, brightness);
|
|
} else {
|
|
return rgbColor;
|
|
}
|
|
}
|
|
|
|
void HdNesPack::GetPixels(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y, HdPpuPixelInfo &pixelInfo, uint32_t *outputBuffer, uint32_t screenWidth)
|
|
{
|
|
HdPackTileInfo *hdPackTileInfo = nullptr;
|
|
HdPackTileInfo *hdPackSpriteInfo = nullptr;
|
|
HdPackData *hdData = Console::GetHdData();
|
|
|
|
bool hasSprite = pixelInfo.SpriteCount > 0;
|
|
if(pixelInfo.Tile.TileIndex != HdPpuTileInfo::NoTile) {
|
|
hdPackTileInfo = GetMatchingTile(screenTiles, x, y, &pixelInfo.Tile);
|
|
}
|
|
|
|
bool hasBgSprite = false;
|
|
int lowestBgSprite = 999;
|
|
|
|
DrawColor(_palette[pixelInfo.Tile.PpuBackgroundColor], outputBuffer, hdData->Scale, screenWidth);
|
|
|
|
if(hasSprite) {
|
|
for(int k = pixelInfo.SpriteCount - 1; k >= 0; k--) {
|
|
if(pixelInfo.Sprite[k].BackgroundPriority) {
|
|
hasBgSprite = true;
|
|
lowestBgSprite = k;
|
|
|
|
hdPackSpriteInfo = GetMatchingTile(screenTiles, x, y, &pixelInfo.Sprite[k]);
|
|
if(hdPackSpriteInfo) {
|
|
DrawTile(pixelInfo.Sprite[k], *hdPackSpriteInfo, outputBuffer, screenWidth);
|
|
} else if(pixelInfo.Sprite[k].SpriteColorIndex != 0) {
|
|
DrawColor(_palette[pixelInfo.Sprite[k].SpriteColor], outputBuffer, hdData->Scale, screenWidth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool hasCustomBackground = _backgroundIndex >= 0 && (y+1)*hdData->Scale <= hdData->Backgrounds[_backgroundIndex].Data->Height && (x+1)*hdData->Scale <= hdData->Backgrounds[_backgroundIndex].Data->Width;
|
|
bool hasNonBackgroundSurrounding = hasCustomBackground ? IsNextToSprite(screenTiles, x, y) : false;
|
|
if(hasCustomBackground) {
|
|
DrawCustomBackground(outputBuffer, x, y, hdData->Scale, screenWidth);
|
|
}
|
|
|
|
if(hdPackTileInfo) {
|
|
DrawTile(pixelInfo.Tile, *hdPackTileInfo, outputBuffer, screenWidth);
|
|
} else {
|
|
//Draw regular SD background tile
|
|
bool useCustomBackground = !hasNonBackgroundSurrounding && hasCustomBackground && pixelInfo.Tile.BgColorIndex == 0;
|
|
if(useCustomBackground) {
|
|
DrawCustomBackground(outputBuffer, x, y, hdData->Scale, screenWidth);
|
|
} else if(pixelInfo.Tile.BgColorIndex != 0 || hasNonBackgroundSurrounding) {
|
|
DrawColor(_palette[pixelInfo.Tile.BgColor], outputBuffer, hdData->Scale, screenWidth);
|
|
}
|
|
}
|
|
|
|
if(hasSprite) {
|
|
for(int k = pixelInfo.SpriteCount - 1; k >= 0; k--) {
|
|
if(!pixelInfo.Sprite[k].BackgroundPriority && lowestBgSprite > k) {
|
|
hdPackSpriteInfo = GetMatchingTile(screenTiles, x, y, &pixelInfo.Sprite[k]);
|
|
if(hdPackSpriteInfo) {
|
|
DrawTile(pixelInfo.Sprite[k], *hdPackSpriteInfo, outputBuffer, screenWidth);
|
|
} else if(pixelInfo.Sprite[k].SpriteColorIndex != 0) {
|
|
DrawColor(_palette[pixelInfo.Sprite[k].SpriteColor], outputBuffer, hdData->Scale, screenWidth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|