#include #include #include #include #include "../System.h" #include "../NLS.h" #include "gb.h" #include "gbCheats.h" #include "gbGlobals.h" #include "../../vbagx.h" #include "../../menu.h" #include "../../fastmath.h" #define CARLLOG bool ColorizeGameboy = true; // The 14-colour palette for a monochrome gameboy game // bg black, bg dark, bg light, bg white, window black, window dark, window light, window white, // obj0 dark, obj0 light, obj0 white, obj1 dark, obj1 light, obj1 white // Only the user sets these colours from emulator menu, and it is only read // by the palette setting functions. u16 systemMonoPalette[14]; // quickly map the 12 possible indices to 15-bit colours // bg index 0..3, obj0 index 0..3, obj1 index 0..3 // This is set internally by the palette setting functions, and used by the // drawing functions. //u16 gbPalette[12]; static bool HadBgPal = false, HadObj0Pal = false, HadObj1Pal = false; const float BL[4] = {0.1f, 0.4f, 0.7f, 1.0f}; // Averages colour with white // factor is from 0 to 1. 0 = no extra whiteness, 1 = 100% whiteness u16 changeColourWhiteness(u16 rgb, float factor) { if (factor==0.0f) return rgb; int f1factor = (0x1f*factor); int factorm1 = 1-factor; int r = (rgb & 0x1f); r = (r * factorm1)+ f1factor; if (r>31) { r=31; } int g = ((rgb >> 5) & 0x1f); g = (g * factorm1)+f1factor; if (g>31) { g=31; } int b = ((rgb >> 10) & 0x1f); b = (b * factorm1)+f1factor; if (b>31) { b=31; } return r | (g << 5) | (b << 10); } // Brightens or darkens a colour // factor is a multiple of normal brightness u16 changeColourBrightness(u16 rgb, float factor) { if (factor==1.0f) return rgb; int bonus = 0; int r = int(float(rgb & 0x1f) * factor); if (r>31) { r=31; bonus+= (1 >> 1); } int g = int(float((rgb >> 5) & 0x1f) * factor); if (g>31) { g=31; bonus+=(1 >> 1); } int b = int(float((rgb >> 10) & 0x1f) * factor); if (b>31) { b=31; bonus += (1 >> 1); } r += bonus; g += bonus; b += bonus; if (r>31) r=31; if (g>31) g=31; if (b>31) b=31; return r | (g << 5) | (b << 10); } u8 oldBgp = 0xFC; // Sets the brightnesses of both the background and window palettes void gbSetBGPalette(u8 value, bool ColoursChanged=false) { static u8 DarkestToBrightestIndex[4] = {3, 2, 1, 0}; static u8 Darkness[4] = {0, 1, 2, 3}; static float BrightnessForBrightest=1, BrightnessForDarkest=0.1; // brightness of palette when indexes are right // darkness of each index (0 = white, 3 = black) gbBgp[0] = value & 0x03; gbBgp[1] = (value & 0x0c)>>2; gbBgp[2] = (value & 0x30)>>4; gbBgp[3] = (value & 0xc0)>>6; // Don't mess with the palette unless we have a Mono gameboy and either the colours changed // or the brightness palette changed. if ((value==oldBgp && !ColoursChanged) || gbCgbMode || gbSgbMode || !ColorizeGameboy) return; bool dup = false; int dupDarkness = -1; if (value!=oldBgp) { #ifdef CARLLOG const char DN[5] = "3210"; log("Bg Pal: %c %c %c %c", DN[gbObp0[0]], DN[gbObp0[1]], DN[gbObp0[2]], DN[gbObp0[3]]); #endif // check for duplicates for (unsigned i=0; i<=2u; ++i) for (unsigned j=i+1; j<=3u; ++j) if (gbBgp[i]==gbBgp[j]) { dup=true; dupDarkness = gbBgp[i]; break; } // We haven't had a full palette yet, so guess... if (dup && !HadBgPal) { if (gbBgp[0]>gbBgp[3]) { DarkestToBrightestIndex[0] = 0; DarkestToBrightestIndex[1] = 1; DarkestToBrightestIndex[2] = 2; DarkestToBrightestIndex[3] = 3; Darkness[0] = gbBgp[0]; Darkness[1] = gbBgp[1]; Darkness[2] = gbBgp[2]; Darkness[3] = gbBgp[3]; } else { DarkestToBrightestIndex[0] = 3; DarkestToBrightestIndex[1] = 2; DarkestToBrightestIndex[2] = 1; DarkestToBrightestIndex[3] = 0; Darkness[0] = gbBgp[3]; Darkness[1] = gbBgp[2]; Darkness[2] = gbBgp[1]; Darkness[3] = gbBgp[0]; } // brightness of brightest colour BrightnessForBrightest = BL[3-gbBgp[DarkestToBrightestIndex[3]]]; BrightnessForDarkest = BL[3-gbBgp[DarkestToBrightestIndex[0]]]; } else if (!dup) { // no duplicates, therefore a complete palette change HadBgPal = true; // now we need to map them from darkest to brightest int Colour = 0; for (int darkness = 3; darkness>=0; --darkness) { for (int index=0; index<=3; ++index) { if (gbBgp[index]==darkness) { DarkestToBrightestIndex[Colour]=index; Darkness[Colour]=gbBgp[index]; ++Colour; break; } } } // brightness of brightest colour BrightnessForBrightest = BL[3-gbBgp[DarkestToBrightestIndex[3]]]; BrightnessForDarkest = BL[3-gbBgp[DarkestToBrightestIndex[0]]]; } else { // duplicates implies fading in or out // since we are really trying to fade, not change palette, // rely on previous DarkestToBrightest list } } float NewBrightnessForBrightest = BL[3-gbBgp[DarkestToBrightestIndex[3]]]; float BrightnessFactor; float WhitenessFactor = 0; // check if they are trying to make the palette whiter if (dupDarkness==0) { // Multiple colours set to white BrightnessFactor = 0; WhitenessFactor += BL[3-gbBgp[0]] + BL[3-gbBgp[1]] + BL[3-gbBgp[2]] + BL[3-gbBgp[3]] - BL[3] - BL[2] - BL[1]- BL[0]; float MaxWhiteness = BL[3] + BL[3] + BL[3] + BL[3] -BL[3] -BL[2] -BL[1] -BL[0]; WhitenessFactor /= (MaxWhiteness+0.4f); // check if they are trying to make the palette brighter } else if (NewBrightnessForBrightest==1.0f && BrightnessForBrightest==1.0f) { float NewBrightnessForDarkest = BL[3-gbBgp[DarkestToBrightestIndex[0]]]; BrightnessFactor = NewBrightnessForDarkest/BrightnessForDarkest; // Perhaps they are trying to make the palette darker } else { BrightnessFactor = NewBrightnessForBrightest/BrightnessForBrightest; } if (WhitenessFactor) { // BG colours for (int colour = 0; colour <= 3; ++colour) { u16 colourRGB = systemMonoPalette[colour]; float colourBrightness = BL[colour]; float indexBrightness = BL[3-Darkness[colour]]; colourRGB = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); gbPalette[0+DarkestToBrightestIndex[colour]] = changeColourWhiteness(colourRGB, WhitenessFactor); } // Window colours for (int colour = 0; colour <= 3; ++colour) { u16 colourRGB = systemMonoPalette[4+colour]; float colourBrightness = BL[colour]; float indexBrightness = BL[3-Darkness[colour]]; colourRGB = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); gbPalette[4+DarkestToBrightestIndex[colour]] = changeColourWhiteness(colourRGB, WhitenessFactor); } } else { // BG colours for (int colour = 0; colour <= 3; ++colour) { u16 colourRGB = systemMonoPalette[colour]; float colourBrightness = BL[colour]; float indexBrightness = BL[3-Darkness[colour]]*BrightnessFactor; gbPalette[0+DarkestToBrightestIndex[colour]] = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); } // Window colours for (int colour = 0; colour <= 3; ++colour) { u16 colourRGB = systemMonoPalette[4+colour]; float colourBrightness = BL[colour]; float indexBrightness = BL[3-Darkness[colour]]*BrightnessFactor; gbPalette[4+DarkestToBrightestIndex[colour]] = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); } } } u8 oldObp0 = 0xFF; void gbSetObj0Palette(u8 value, bool ColoursChanged = false) { static u8 DarkestToBrightestIndex[3] = {3, 2, 1}; static u8 Darkness[3] = {1, 2, 3}; static float BrightnessForBrightest, BrightnessForDarkest; // brightness of palette when indexes are right // darkness of each index (0 = white, 3 = black) gbObp0[0] = value & 0x03; gbObp0[1] = (value & 0x0c)>>2; gbObp0[2] = (value & 0x30)>>4; gbObp0[3] = (value & 0xc0)>>6; // Don't mess with the palette unless we have a Mono gameboy and either the colours changed // or the brightness palette changed. if ((value==oldObp0 && !ColoursChanged) || gbCgbMode || gbSgbMode || !ColorizeGameboy) return; bool dup = false; int dupDarkness = -1; if (value!=oldObp0) { #ifdef CARLLOG const char DN[5] = "3210"; log("Obj0 Pal: %c %c %c", DN[gbObp0[1]], DN[gbObp0[2]], DN[gbObp0[3]]); #endif // check for duplicates for (int i=1; i<=2; i++) for (int j=i+1; j<=3; j++) if (gbObp0[i]==gbObp0[j]) { dup=true; dupDarkness = gbObp0[i]; break; } // We haven't had a full palette yet, so guess... if (dup && !HadObj1Pal) { if (gbObp0[1]>gbObp0[3]) { DarkestToBrightestIndex[0] = 1; DarkestToBrightestIndex[1] = 2; DarkestToBrightestIndex[2] = 3; Darkness[0] = gbObp0[1]; Darkness[1] = gbObp0[2]; Darkness[2] = gbObp0[3]; } else { DarkestToBrightestIndex[0] = 3; DarkestToBrightestIndex[1] = 2; DarkestToBrightestIndex[2] = 1; Darkness[0] = gbObp0[3]; Darkness[1] = gbObp0[2]; Darkness[2] = gbObp0[1]; } // brightness of brightest colour BrightnessForBrightest = BL[3-gbObp0[DarkestToBrightestIndex[2]]]; BrightnessForDarkest = BL[3-gbObp0[DarkestToBrightestIndex[0]]]; } else if (!dup) { // no duplicates, therefore a complete brightness palette change HadObj0Pal = true; // now we need to map them from darkest to brightest int Colour = 0; for (int darkness = 3; darkness>=0; --darkness) { for (int index=1; index<=3; index++) { if (gbObp0[index]==darkness) { DarkestToBrightestIndex[Colour]=index; Darkness[Colour]=gbObp0[index]; ++Colour; break; } } } // brightness of brightest colour BrightnessForBrightest = BL[3-gbObp0[DarkestToBrightestIndex[2]]]; BrightnessForDarkest = BL[3-gbObp0[DarkestToBrightestIndex[0]]]; } else { // duplicates implies fading in or out // since we are really trying to fade, not change palette, // rely on previous DarkestToBrightest list } } float NewBrightnessForBrightest = BL[3-gbObp0[DarkestToBrightestIndex[2]]]; float BrightnessFactor; float WhitenessFactor = 0; // check if they are trying to make the palette whiter if (dupDarkness==0) { // Multiple colours set to white BrightnessFactor = 0; WhitenessFactor+= BL[3-gbObp0[1]] + BL[3-gbObp0[2]] + BL[3-gbObp0[3]] -BL[3] -BL[2] -BL[1]; float MaxWhiteness = BL[3] + BL[3] + BL[3] - BL[3] - BL[2] - BL[1]; WhitenessFactor /= (MaxWhiteness+0.3f); // check if they are trying to make the palette brighter } else if (NewBrightnessForBrightest==1.0f && BrightnessForBrightest==1.0f) { float NewBrightnessForDarkest = BL[3-gbObp0[DarkestToBrightestIndex[0]]]; BrightnessFactor = NewBrightnessForDarkest/BrightnessForDarkest; } else { BrightnessFactor = NewBrightnessForBrightest/BrightnessForBrightest; } if (WhitenessFactor) { for (int colour = 0; colour <= 2; colour++) { u16 colourRGB = systemMonoPalette[colour+8]; float colourBrightness = BL[colour+1]; float indexBrightness = BL[3-Darkness[colour]]; colourRGB = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); gbPalette[8+DarkestToBrightestIndex[colour]] = changeColourWhiteness(colourRGB, WhitenessFactor); } } else { for (int colour = 0; colour <= 2; colour++) { u16 colourRGB = systemMonoPalette[colour+8]; float colourBrightness = BL[colour+1]; float indexBrightness = BL[3-Darkness[colour]]*BrightnessFactor; gbPalette[8+DarkestToBrightestIndex[colour]] = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); } } gbPalette[8] = 0; // always transparent } u8 oldObp1 = 0xFF; void gbSetObj1Palette(u8 value, bool ColoursChanged = false) { static u8 DarkestToBrightestIndex[3] = {3, 2, 1}; static u8 Darkness[3] = {1, 2, 3}; static float BrightnessForBrightest, BrightnessForDarkest; // brightness of palette when indexes are right // darkness of each index (0 = white, 3 = black) gbObp1[0] = value & 0x03; gbObp1[1] = (value & 0x0c)>>2; gbObp1[2] = (value & 0x30)>>4; gbObp1[3] = (value & 0xc0)>>6; // Don't mess with the palette unless we have a Mono gameboy and either the colours changed // or the brightness palette changed. if ((value==oldObp1 && !ColoursChanged) || gbCgbMode || gbSgbMode || !ColorizeGameboy) return; bool dup = false; int dupDarkness = -1; if (value!=oldObp1) { #ifdef CARLLOG const char DN[5] = "3210"; log("Obj1 Pal: %c %c %c", DN[gbObp1[1]], DN[gbObp1[2]], DN[gbObp1[3]]); #endif // check for duplicates for (int i=1; i<=2; i++) for (int j=i+1; j<=3; j++) if (gbObp1[i]==gbObp1[j]) { dup=true; dupDarkness=gbObp1[i]; break; } // We haven't had a full palette yet, so guess... if (dup && !HadObj1Pal) { if (gbObp0[1]>gbObp0[3]) { DarkestToBrightestIndex[0] = 1; DarkestToBrightestIndex[1] = 2; DarkestToBrightestIndex[2] = 3; Darkness[0] = gbObp0[1]; Darkness[1] = gbObp0[2]; Darkness[2] = gbObp0[3]; } else { DarkestToBrightestIndex[0] = 3; DarkestToBrightestIndex[1] = 2; DarkestToBrightestIndex[2] = 1; Darkness[0] = gbObp0[3]; Darkness[1] = gbObp0[2]; Darkness[2] = gbObp0[1]; } // brightness of brightest colour BrightnessForBrightest = BL[3-gbObp1[DarkestToBrightestIndex[2]]]; BrightnessForDarkest = BL[3-gbObp1[DarkestToBrightestIndex[0]]]; } else if (!dup) { // no duplicates, therefore a complete palette change HadObj1Pal = true; // now we need to map them from darkest to brightest int Colour = 0; for (int darkness = 3; darkness>=0; darkness--) { for (int index=1; index<=3; index++) { if (gbObp1[index]==darkness) { DarkestToBrightestIndex[Colour]=index; Darkness[Colour]=gbObp1[index]; Colour++; break; } } } // brightness of brightest colour BrightnessForBrightest = BL[3-gbObp1[DarkestToBrightestIndex[2]]]; BrightnessForDarkest = BL[3-gbObp1[DarkestToBrightestIndex[0]]]; } else { // duplicates implies fading in or out // since we are really trying to fade, not change palette, // rely on previous DarkestToBrightest list } } float NewBrightnessForBrightest = BL[3-gbObp1[DarkestToBrightestIndex[2]]]; float BrightnessFactor; float WhitenessFactor = 0; // check if they are trying to make the palette whiter if (dupDarkness==0) { // Multiple colours set to white BrightnessFactor = 0; WhitenessFactor+= BL[3-gbObp1[1]] + BL[3-gbObp1[2]] + BL[3-gbObp1[3]] -BL[3] -BL[2] -BL[1]; float MaxWhiteness = BL[3] + BL[3] + BL[3] - BL[3] - BL[2] - BL[1]; WhitenessFactor /= (MaxWhiteness+0.3f); // check if they are trying to make the palette brighter } else if (NewBrightnessForBrightest==1.0f && BrightnessForBrightest==1.0f) { float NewBrightnessForDarkest = BL[3-gbObp1[DarkestToBrightestIndex[0]]]; BrightnessFactor = NewBrightnessForDarkest/BrightnessForDarkest; } else { BrightnessFactor = NewBrightnessForBrightest/BrightnessForBrightest; } if (WhitenessFactor) { for (int colour = 0; colour <= 2; colour++) { u16 colourRGB = systemMonoPalette[colour+11]; float colourBrightness = BL[colour+1]; float indexBrightness = BL[3-Darkness[colour]]; colourRGB = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); gbPalette[12+DarkestToBrightestIndex[colour]] = changeColourWhiteness(colourRGB, WhitenessFactor); } } else { for (int colour = 0; colour <= 2; colour++) { u16 colourRGB = systemMonoPalette[colour+11]; float colourBrightness = BL[colour+1]; float indexBrightness = BL[3-Darkness[colour]]*BrightnessFactor; gbPalette[12+DarkestToBrightestIndex[colour]] = changeColourBrightness(colourRGB, indexBrightness/colourBrightness); } } gbPalette[12] = 0; // always transparent } bool StartColorizing() { if ((!GCSettings.colorize) || gbSgbMode || gbCgbMode) return false; if (ColorizeGameboy) return true; ColorizeGameboy = true; gbSetBGPalette(oldBgp); gbSetObj0Palette(oldObp0); gbSetObj1Palette(oldObp1); return true; } void StopColorizing() { if(!ColorizeGameboy || gbSgbMode || gbCgbMode) return; int gbpo12 = gbPaletteOption*12; for(int i = 0; i < 12; i+=4){ gbPalette[i] = systemGbPalette[gbpo12+i]; gbPalette[i+1] = systemGbPalette[gbpo12+i+1]; gbPalette[i+2] = systemGbPalette[gbpo12+i+2]; gbPalette[i+3] = systemGbPalette[gbpo12+i+3]; } ColorizeGameboy = false; } // convert 0xRRGGBB to our 15 bit format u16 Make15Bit(u32 rgb) { return ((rgb >> 19) & 0x1F) | (((rgb >> 11) & 0x1F) << 5) | (((rgb >> 3) & 0x1F) << 10); } u32 OldObpBright[2]={0}, OldObpMedium[2]={0}, OldObpDark[2]={0}; void gbSetSpritePal(u8 WhichPal, u32 bright, u32 medium, u32 dark) { if (!StartColorizing()) return; // cancel if we already set to these colours if (WhichPal>0) { int p = WhichPal-1; if(((OldObpBright[p] - bright) | (OldObpMedium[p] - medium) | (OldObpDark[p] - dark)) == 0){ return; } } int index = 0; // check if we are setting both sprite palettes at once if (WhichPal==0) { gbSetSpritePal(1, bright, medium, dark); gbSetSpritePal(2, bright, medium, dark); return; } else if (WhichPal==1) index = 8; // Obj0 else if (WhichPal==2) index=11; // Obj1 // save colours as 15 bit systemMonoPalette[index]=Make15Bit(dark); systemMonoPalette[index+1]=Make15Bit(medium); systemMonoPalette[index+2]=Make15Bit(bright); // change the brightness palette if (WhichPal==1) { u8 old = oldObp0; oldObp0 +=1; gbSetObj0Palette(old); } else if (WhichPal==2) { u8 old = oldObp1; oldObp1 +=1; gbSetObj1Palette(old); } } void gbSetSpritePal(u8 WhichPal, u32 bright) { u8 r = (bright >> 16) & 0xFF; u8 g = (bright >> 8) & 0xFF; u8 b = (bright >> 0) & 0xFF; u32 medium = (((u32)(r*0.7f)) << 16) | (((u32)(g*0.7f)) << 8) | (((u32)(b*0.7f)) << 0); u32 dark = (((u32)(r*0.4f)) << 16) | (((u32)(g*0.4f)) << 8) | (((u32)(b*0.4f)) << 0); gbSetSpritePal(WhichPal, bright, medium, dark); } u32 OldBgBright=0, OldBgMedium=0, OldBgDark=0, OldBgBlack=0; void gbSetBgPal(u8 WhichPal, u32 bright, u32 medium, u32 dark, u32 black=0x000000) { if (!StartColorizing()) return; if (OldBgBright==bright && OldBgMedium==medium && OldBgDark==dark && OldBgBlack==black) return; int index = 0; if (WhichPal==0) { gbSetBgPal(1, bright, medium, dark, black); gbSetBgPal(2, bright, medium, dark, black); return; } else if (WhichPal==1) index = 0; // background else if (WhichPal==2) index=4; // window systemMonoPalette[index]=Make15Bit(black); systemMonoPalette[index+1]=Make15Bit(dark); systemMonoPalette[index+2]=Make15Bit(medium); systemMonoPalette[index+3]=Make15Bit(bright); gbSetBGPalette(oldBgp, true); OldBgBright = bright; OldBgMedium = medium; OldBgDark = dark; OldBgBlack = black; } void gbSetBgPal(u8 WhichPal, u32 bright) { u8 r = (bright >> 16) & 0xFF; u8 g = (bright >> 8) & 0xFF; u8 b = (bright >> 0) & 0xFF; u32 medium = ((ftou((utof(r)*0.7f))) << 16) | ((ftou((utof(g)*0.7f))) << 8) | ((ftou((utof(b)*0.7f))) << 0); u32 dark = ((ftou((utof(r)*0.4f))) << 16) | ((ftou((utof(g)*0.4f))) << 8) | ((ftou((utof(b)*0.4f))) << 0); u32 black = ((ftou((utof(r)*0.1f))) << 16) | ((ftou((utof(g)*0.1f))) << 8) | ((ftou((utof(b)*0.1f))) << 0); gbSetBgPal(WhichPal, bright, medium, dark, black); } // Set the whole 14-colour palette void gbSetPalette(u32 RRGGBB[]) { gbSetBgPal(1, RRGGBB[0], RRGGBB[1], RRGGBB[2], RRGGBB[3]); gbSetBgPal(2, RRGGBB[4], RRGGBB[5], RRGGBB[6], RRGGBB[7]); gbSetSpritePal(1, RRGGBB[8], RRGGBB[9], RRGGBB[10]); gbSetSpritePal(2, RRGGBB[11], RRGGBB[12], RRGGBB[13]); } void gbPaletteReset() { HadBgPal = HadObj0Pal = HadObj1Pal = false; oldBgp = 0xFC; oldObp0 = oldObp1 = 0xFF; OldBgBright=0; OldBgMedium=0; OldBgDark=0; OldBgBlack=0; OldObpBright[0]= OldObpBright[1]= OldObpMedium[0]= OldObpMedium[1]= OldObpDark[0]= OldObpDark[1] =0; }