diff --git a/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj index ec39aae33..658baedc9 100644 --- a/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj +++ b/desmume/src/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj @@ -1168,6 +1168,16 @@ ABD59849187D4A6C00069403 /* Icon_PaddleKnob_256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = ABD59846187D4A6C00069403 /* Icon_PaddleKnob_256x256.png */; }; ABD5984A187D4A6C00069403 /* Icon_PaddleKnob_256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = ABD59846187D4A6C00069403 /* Icon_PaddleKnob_256x256.png */; }; ABD5984B187D4A6C00069403 /* Icon_PaddleKnob_256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = ABD59846187D4A6C00069403 /* Icon_PaddleKnob_256x256.png */; }; + ABECB50918A460710052D52A /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB50818A460710052D52A /* xbrz.cpp */; }; + ABECB50A18A460710052D52A /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB50818A460710052D52A /* xbrz.cpp */; }; + ABECB50B18A460710052D52A /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB50818A460710052D52A /* xbrz.cpp */; }; + ABECB50C18A460710052D52A /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB50818A460710052D52A /* xbrz.cpp */; }; + ABECB50D18A460710052D52A /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB50818A460710052D52A /* xbrz.cpp */; }; + ABECB51418A460910052D52A /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */; }; + ABECB51518A460910052D52A /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */; }; + ABECB51618A460910052D52A /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */; }; + ABECB51718A460910052D52A /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */; }; + ABECB51818A460910052D52A /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */; }; ABEF84721873576300E99ADC /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB8C6E56186CD07E00E3EC64 /* ForceFeedback.framework */; }; ABEF84831873578F00E99ADC /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB8C6E56186CD07E00E3EC64 /* ForceFeedback.framework */; }; ABEF84841873579400E99ADC /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB8C6E56186CD07E00E3EC64 /* ForceFeedback.framework */; }; @@ -1624,6 +1634,10 @@ ABE6702A1415DE6C00E8E4C9 /* tinyxmlparser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tinyxmlparser.cpp; sourceTree = ""; }; ABE7F53C13EE1C7900FD3A71 /* cocoa_firmware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_firmware.h; sourceTree = ""; }; ABE7F53D13EE1C7900FD3A71 /* cocoa_firmware.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = cocoa_firmware.mm; sourceTree = ""; }; + ABECB50718A460710052D52A /* xbrz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbrz.h; sourceTree = ""; }; + ABECB50818A460710052D52A /* xbrz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xbrz.cpp; sourceTree = ""; }; + ABECB51218A460910052D52A /* OGLDisplayOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OGLDisplayOutput.h; sourceTree = ""; }; + ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OGLDisplayOutput.cpp; sourceTree = ""; }; ABEFCF5D141AB82A000CC0CD /* AppIcon_ROMSave.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon_ROMSave.icns; sourceTree = ""; }; ABEFCF5E141AB82A000CC0CD /* AppIcon_DeSmuME.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon_DeSmuME.icns; sourceTree = ""; }; ABEFCF5F141AB82A000CC0CD /* AppIcon_NintendoDS_ROM.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon_NintendoDS_ROM.icns; sourceTree = ""; }; @@ -1812,6 +1826,7 @@ AB2145221714DFF4006DDB0F /* audiosamplegenerator.cpp */, ABD0A5341501AA5A0074A094 /* coreaudiosound.cpp */, ABD9A46413DB99B300777194 /* mic_ext.cpp */, + ABECB51318A460910052D52A /* OGLDisplayOutput.cpp */, ABD0A5351501AA5A0074A094 /* ringbuffer.cpp */, ABD104141346652500AF11D1 /* sndOSX.cpp */, AB2145211714DFF4006DDB0F /* audiosamplegenerator.h */, @@ -1829,6 +1844,7 @@ ABE5DFE3143FB1DA00835AD8 /* cocoa_videofilter.h */, ABD0A5361501AA5A0074A094 /* coreaudiosound.h */, ABD9A46313DB99B300777194 /* mic_ext.h */, + ABECB51218A460910052D52A /* OGLDisplayOutput.h */, ABD0A5371501AA5A0074A094 /* ringbuffer.h */, ABD104011346652500AF11D1 /* sndOSX.h */, AB2F56EE1704C86900E28885 /* utilities.h */, @@ -2451,12 +2467,14 @@ ABFE150414C92FF5005D6699 /* lq2x.cpp */, ABFE150614C92FF5005D6699 /* scanline.cpp */, AB817A35143EE2DB00A7DFE9 /* videofilter.cpp */, + ABECB50818A460710052D52A /* xbrz.cpp */, ABFE14FD14C92FF5005D6699 /* filter.h */, ABFE14FF14C92FF5005D6699 /* hq2x.h */, ABFE150214C92FF5005D6699 /* hq4x.h */, ABFE150314C92FF5005D6699 /* interp.h */, ABFE150514C92FF5005D6699 /* lq2x.h */, AB817A34143EE2DB00A7DFE9 /* videofilter.h */, + ABECB50718A460710052D52A /* xbrz.h */, ); name = filter; path = ../filter; @@ -3366,6 +3384,8 @@ AB53518A18313E4E00CCD532 /* slot2.cpp in Sources */, ABAE2F7F18682B6C00C92F4F /* Slot2WindowDelegate.mm in Sources */, ABAE2F8818682B8F00C92F4F /* cocoa_slot2.mm in Sources */, + ABECB50D18A460710052D52A /* xbrz.cpp in Sources */, + ABECB51818A460910052D52A /* OGLDisplayOutput.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3508,6 +3528,8 @@ AB53518718313E4E00CCD532 /* slot2.cpp in Sources */, ABAE2F7B18682B6C00C92F4F /* Slot2WindowDelegate.mm in Sources */, ABAE2F8418682B8F00C92F4F /* cocoa_slot2.mm in Sources */, + ABECB50A18A460710052D52A /* xbrz.cpp in Sources */, + ABECB51518A460910052D52A /* OGLDisplayOutput.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3680,6 +3702,8 @@ ABAE2F7C18682B6C00C92F4F /* Slot2WindowDelegate.mm in Sources */, ABAE2F8518682B8F00C92F4F /* cocoa_slot2.mm in Sources */, AB8FFCB4186F8E5400C10085 /* slot2_mpcf.cpp in Sources */, + ABECB50918A460710052D52A /* xbrz.cpp in Sources */, + ABECB51418A460910052D52A /* OGLDisplayOutput.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3852,6 +3876,8 @@ AB53518918313E4E00CCD532 /* slot2.cpp in Sources */, ABAE2F7E18682B6C00C92F4F /* Slot2WindowDelegate.mm in Sources */, ABAE2F8718682B8F00C92F4F /* cocoa_slot2.mm in Sources */, + ABECB50C18A460710052D52A /* xbrz.cpp in Sources */, + ABECB51718A460910052D52A /* OGLDisplayOutput.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3994,6 +4020,8 @@ AB53518818313E4E00CCD532 /* slot2.cpp in Sources */, ABAE2F7D18682B6C00C92F4F /* Slot2WindowDelegate.mm in Sources */, ABAE2F8618682B8F00C92F4F /* cocoa_slot2.mm in Sources */, + ABECB50B18A460710052D52A /* xbrz.cpp in Sources */, + ABECB51618A460910052D52A /* OGLDisplayOutput.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj index 080076dc1..988774f0e 100644 --- a/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj +++ b/desmume/src/cocoa/DeSmuME (XCode 4).xcodeproj/project.pbxproj @@ -533,6 +533,10 @@ ABB3C6D51501C04F00E0C22E /* thumb_instructions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FECD1345AC8400AF11D1 /* thumb_instructions.cpp */; }; ABB3C6D61501C04F00E0C22E /* version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FECE1345AC8400AF11D1 /* version.cpp */; }; ABB3C6D71501C04F00E0C22E /* wifi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FECF1345AC8400AF11D1 /* wifi.cpp */; }; + ABB72D4218A493A900EB9AA7 /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABB72D4118A493A900EB9AA7 /* OGLDisplayOutput.cpp */; }; + ABB72D4318A493A900EB9AA7 /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABB72D4118A493A900EB9AA7 /* OGLDisplayOutput.cpp */; }; + ABB72D4618A493C000EB9AA7 /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABB72D4518A493C000EB9AA7 /* xbrz.cpp */; }; + ABB72D4718A493C000EB9AA7 /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABB72D4518A493C000EB9AA7 /* xbrz.cpp */; }; ABB97876144E89CC00793FA3 /* Icon_ActionReplay_32x32.png in Resources */ = {isa = PBXBuildFile; fileRef = ABB97873144E89CC00793FA3 /* Icon_ActionReplay_32x32.png */; }; ABB97877144E89CC00793FA3 /* Icon_CodeBreaker_32x32.png in Resources */ = {isa = PBXBuildFile; fileRef = ABB97874144E89CC00793FA3 /* Icon_CodeBreaker_32x32.png */; }; ABB97878144E89CC00793FA3 /* Icon_DeSmuME_32x32.png in Resources */ = {isa = PBXBuildFile; fileRef = ABB97875144E89CC00793FA3 /* Icon_DeSmuME_32x32.png */; }; @@ -931,6 +935,10 @@ ABB3C6401501BB8300E0C22E /* OESoundInterface.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OESoundInterface.mm; sourceTree = ""; }; ABB3C6411501BB8300E0C22E /* OESoundInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OESoundInterface.h; sourceTree = ""; }; ABB3C6471501BC6D00E0C22E /* DeSmuME.oecoreplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DeSmuME.oecoreplugin; sourceTree = BUILT_PRODUCTS_DIR; }; + ABB72D4018A493A900EB9AA7 /* OGLDisplayOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OGLDisplayOutput.h; sourceTree = ""; }; + ABB72D4118A493A900EB9AA7 /* OGLDisplayOutput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OGLDisplayOutput.cpp; sourceTree = ""; }; + ABB72D4418A493C000EB9AA7 /* xbrz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbrz.h; sourceTree = ""; }; + ABB72D4518A493C000EB9AA7 /* xbrz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xbrz.cpp; sourceTree = ""; }; ABB97873144E89CC00793FA3 /* Icon_ActionReplay_32x32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon_ActionReplay_32x32.png; path = Images/Icon_ActionReplay_32x32.png; sourceTree = ""; }; ABB97874144E89CC00793FA3 /* Icon_CodeBreaker_32x32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon_CodeBreaker_32x32.png; path = Images/Icon_CodeBreaker_32x32.png; sourceTree = ""; }; ABB97875144E89CC00793FA3 /* Icon_DeSmuME_32x32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon_DeSmuME_32x32.png; path = Images/Icon_DeSmuME_32x32.png; sourceTree = ""; }; @@ -1248,6 +1256,7 @@ AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */, AB23567216C2F6F400DA782E /* macosx_10_5_compat.cpp */, ABD10AE61715FCDD00B5729D /* mic_ext.cpp */, + ABB72D4118A493A900EB9AA7 /* OGLDisplayOutput.cpp */, AB1B9E601501A78000464647 /* ringbuffer.cpp */, ABD104141346652500AF11D1 /* sndOSX.cpp */, ABD10AE31715FCDD00B5729D /* audiosamplegenerator.h */, @@ -1265,6 +1274,7 @@ ABE5DFE3143FB1DA00835AD8 /* cocoa_videofilter.h */, AB1B9E611501A78000464647 /* coreaudiosound.h */, ABD10AE41715FCDD00B5729D /* mic_ext.h */, + ABB72D4018A493A900EB9AA7 /* OGLDisplayOutput.h */, AB1B9E621501A78000464647 /* ringbuffer.h */, ABD104011346652500AF11D1 /* sndOSX.h */, AB82445E1704AEC400B8EE20 /* utilities.h */, @@ -1902,12 +1912,14 @@ ABFE150414C92FF5005D6699 /* lq2x.cpp */, ABFE150614C92FF5005D6699 /* scanline.cpp */, AB817A35143EE2DB00A7DFE9 /* videofilter.cpp */, + ABB72D4518A493C000EB9AA7 /* xbrz.cpp */, ABFE14FD14C92FF5005D6699 /* filter.h */, ABFE14FF14C92FF5005D6699 /* hq2x.h */, ABFE150214C92FF5005D6699 /* hq4x.h */, ABFE150314C92FF5005D6699 /* interp.h */, ABFE150514C92FF5005D6699 /* lq2x.h */, AB817A34143EE2DB00A7DFE9 /* videofilter.h */, + ABB72D4418A493C000EB9AA7 /* xbrz.h */, ); name = filter; path = ../filter; @@ -2311,6 +2323,7 @@ ABD1FED31345AC8400AF11D1 /* armcpu.cpp in Sources */, ABD1FED41345AC8400AF11D1 /* bios.cpp in Sources */, ABD1FF5B1345ACBF00AF11D1 /* cache.cpp in Sources */, + ABB72D4318A493A900EB9AA7 /* OGLDisplayOutput.cpp in Sources */, ABD1FED51345AC8400AF11D1 /* cheatSystem.cpp in Sources */, ABD1FED71345AC8400AF11D1 /* common.cpp in Sources */, ABD1FED81345AC8400AF11D1 /* cp15.cpp in Sources */, @@ -2424,6 +2437,7 @@ AB405634169F5DBB0016AC3E /* compiler.cpp in Sources */, AB405637169F5DBB0016AC3E /* compilercontext.cpp in Sources */, AB40563A169F5DBB0016AC3E /* compilerfunc.cpp in Sources */, + ABB72D4718A493C000EB9AA7 /* xbrz.cpp in Sources */, AB40563D169F5DBB0016AC3E /* compileritem.cpp in Sources */, AB405640169F5DBB0016AC3E /* context.cpp in Sources */, AB405643169F5DBB0016AC3E /* cpuinfo.cpp in Sources */, @@ -2550,6 +2564,7 @@ AB796D3515CDCBA200C59155 /* sndOSX.cpp in Sources */, AB796D3615CDCBA200C59155 /* SndOut.cpp in Sources */, AB796D3715CDCBA200C59155 /* SoundTouch.cpp in Sources */, + ABB72D4618A493C000EB9AA7 /* xbrz.cpp in Sources */, AB796D3815CDCBA200C59155 /* SPU.cpp in Sources */, AB796D3915CDCBA200C59155 /* sse_optimized.cpp in Sources */, AB796D3A15CDCBA200C59155 /* task.cpp in Sources */, @@ -2583,6 +2598,7 @@ AB796D5A15CDCBA200C59155 /* preferencesWindowDelegate.mm in Sources */, AB796D5B15CDCBA200C59155 /* 2xsai.cpp in Sources */, AB796D5C15CDCBA200C59155 /* bilinear.cpp in Sources */, + ABB72D4218A493A900EB9AA7 /* OGLDisplayOutput.cpp in Sources */, AB796D5D15CDCBA200C59155 /* epx.cpp in Sources */, AB796D5E15CDCBA200C59155 /* hq2x.cpp in Sources */, AB796D5F15CDCBA200C59155 /* hq4x.cpp in Sources */, diff --git a/desmume/src/cocoa/DeSmuME (Xcode 5).xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME (Xcode 5).xcodeproj/project.pbxproj index cf9da5235..09fe29319 100644 --- a/desmume/src/cocoa/DeSmuME (Xcode 5).xcodeproj/project.pbxproj +++ b/desmume/src/cocoa/DeSmuME (Xcode 5).xcodeproj/project.pbxproj @@ -195,6 +195,8 @@ AB405694169F5DCC0016AC3E /* x86util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB405676169F5DCC0016AC3E /* x86util.cpp */; }; AB405695169F5DCC0016AC3E /* x86util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB405676169F5DCC0016AC3E /* x86util.cpp */; }; AB4676F314AB12D60002FF94 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0A0D1914AACA9600E83E91 /* libz.dylib */; }; + AB47B52E18A3F722009A42AF /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB47B52C18A3F722009A42AF /* xbrz.cpp */; }; + AB47B52F18A45C35009A42AF /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB47B52C18A3F722009A42AF /* xbrz.cpp */; }; AB4FCEBD1692AB82000F498F /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB4FCEBC1692AB82000F498F /* Accelerate.framework */; }; AB4FCEBE1692AB82000F498F /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB4FCEBC1692AB82000F498F /* Accelerate.framework */; }; AB4FCEBF1692AB82000F498F /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB4FCEBC1692AB82000F498F /* Accelerate.framework */; }; @@ -683,6 +685,8 @@ ABE6702C1415DE6C00E8E4C9 /* tinyxml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABE670271415DE6C00E8E4C9 /* tinyxml.cpp */; }; ABE6702D1415DE6C00E8E4C9 /* tinyxmlerror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABE670291415DE6C00E8E4C9 /* tinyxmlerror.cpp */; }; ABE6702E1415DE6C00E8E4C9 /* tinyxmlparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABE6702A1415DE6C00E8E4C9 /* tinyxmlparser.cpp */; }; + ABE6840C189E33BC007FD69C /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABE6840B189E33BC007FD69C /* OGLDisplayOutput.cpp */; }; + ABE6840D189E33BC007FD69C /* OGLDisplayOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABE6840B189E33BC007FD69C /* OGLDisplayOutput.cpp */; }; ABE7F53E13EE1C7900FD3A71 /* cocoa_firmware.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABE7F53D13EE1C7900FD3A71 /* cocoa_firmware.mm */; }; ABE9EEEA1501C6EB00D3FB19 /* cocoa_firmware.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABE7F53D13EE1C7900FD3A71 /* cocoa_firmware.mm */; }; ABE9EEEB1501C78700D3FB19 /* fs-linux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABD1FEB21345AC8400AF11D1 /* fs-linux.cpp */; }; @@ -864,6 +868,8 @@ AB405675169F5DCC0016AC3E /* x86operand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x86operand.h; sourceTree = ""; }; AB405676169F5DCC0016AC3E /* x86util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x86util.cpp; sourceTree = ""; }; AB405677169F5DCC0016AC3E /* x86util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x86util.h; sourceTree = ""; }; + AB47B52B18A3F722009A42AF /* xbrz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbrz.h; sourceTree = ""; }; + AB47B52C18A3F722009A42AF /* xbrz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xbrz.cpp; sourceTree = ""; }; AB4FCEBC1692AB82000F498F /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; AB5648FD186E6EA8002740F4 /* cocoa_slot2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_slot2.h; sourceTree = ""; }; AB5648FE186E6EA8002740F4 /* cocoa_slot2.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = cocoa_slot2.mm; sourceTree = ""; }; @@ -1159,6 +1165,8 @@ ABE670281415DE6C00E8E4C9 /* tinyxml.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinyxml.h; sourceTree = ""; }; ABE670291415DE6C00E8E4C9 /* tinyxmlerror.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tinyxmlerror.cpp; sourceTree = ""; }; ABE6702A1415DE6C00E8E4C9 /* tinyxmlparser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tinyxmlparser.cpp; sourceTree = ""; }; + ABE6840B189E33BC007FD69C /* OGLDisplayOutput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OGLDisplayOutput.cpp; sourceTree = ""; }; + ABE6840E189E33D5007FD69C /* OGLDisplayOutput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OGLDisplayOutput.h; sourceTree = ""; }; ABE7F53C13EE1C7900FD3A71 /* cocoa_firmware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocoa_firmware.h; sourceTree = ""; }; ABE7F53D13EE1C7900FD3A71 /* cocoa_firmware.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = cocoa_firmware.mm; sourceTree = ""; }; ABEFCF5D141AB82A000CC0CD /* AppIcon_ROMSave.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon_ROMSave.icns; sourceTree = ""; }; @@ -1247,6 +1255,7 @@ AB1B9E5F1501A78000464647 /* coreaudiosound.cpp */, AB23567216C2F6F400DA782E /* macosx_10_5_compat.cpp */, ABD10AE61715FCDD00B5729D /* mic_ext.cpp */, + ABE6840B189E33BC007FD69C /* OGLDisplayOutput.cpp */, AB1B9E601501A78000464647 /* ringbuffer.cpp */, ABD104141346652500AF11D1 /* sndOSX.cpp */, ABD10AE31715FCDD00B5729D /* audiosamplegenerator.h */, @@ -1265,6 +1274,7 @@ AB1B9E611501A78000464647 /* coreaudiosound.h */, ABD10AE41715FCDD00B5729D /* mic_ext.h */, AB1B9E621501A78000464647 /* ringbuffer.h */, + ABE6840E189E33D5007FD69C /* OGLDisplayOutput.h */, ABD104011346652500AF11D1 /* sndOSX.h */, AB82445E1704AEC400B8EE20 /* utilities.h */, ABA6574A14511EC90077E5E9 /* cocoa_cheat.mm */, @@ -1901,12 +1911,14 @@ ABFE150414C92FF5005D6699 /* lq2x.cpp */, ABFE150614C92FF5005D6699 /* scanline.cpp */, AB817A35143EE2DB00A7DFE9 /* videofilter.cpp */, + AB47B52C18A3F722009A42AF /* xbrz.cpp */, ABFE14FD14C92FF5005D6699 /* filter.h */, ABFE14FF14C92FF5005D6699 /* hq2x.h */, ABFE150214C92FF5005D6699 /* hq4x.h */, ABFE150314C92FF5005D6699 /* interp.h */, ABFE150514C92FF5005D6699 /* lq2x.h */, AB817A34143EE2DB00A7DFE9 /* videofilter.h */, + AB47B52B18A3F722009A42AF /* xbrz.h */, ); name = filter; path = ../filter; @@ -2371,6 +2383,7 @@ ABD1FF0F1345AC9C00AF11D1 /* slot2_gbagame.cpp in Sources */, ABD1FF101345AC9C00AF11D1 /* slot2_guitarGrip.cpp in Sources */, ABD1FF111345AC9C00AF11D1 /* slot2_mpcf.cpp in Sources */, + ABE6840D189E33BC007FD69C /* OGLDisplayOutput.cpp in Sources */, ABD1FF121345AC9C00AF11D1 /* slot2_none.cpp in Sources */, ABD1FF131345AC9C00AF11D1 /* slot2_paddle.cpp in Sources */, ABD1FF141345AC9C00AF11D1 /* slot2_piano.cpp in Sources */, @@ -2423,6 +2436,7 @@ ABFE150B14C92FF5005D6699 /* hq4x.cpp in Sources */, AB9038A717C5ECFD00F410BD /* advanscene.cpp in Sources */, ABFE150D14C92FF5005D6699 /* lq2x.cpp in Sources */, + AB47B52E18A3F722009A42AF /* xbrz.cpp in Sources */, ABFE150E14C92FF5005D6699 /* scanline.cpp in Sources */, AB1B9E631501A78000464647 /* coreaudiosound.cpp in Sources */, AB1B9E661501A78000464647 /* ringbuffer.cpp in Sources */, @@ -2547,7 +2561,9 @@ AB5648FF186E6EA8002740F4 /* cocoa_slot2.mm in Sources */, AB796D2A15CDCBA200C59155 /* slot1_r4.cpp in Sources */, AB796D2C15CDCBA200C59155 /* slot1_retail_nand.cpp in Sources */, + ABE6840C189E33BC007FD69C /* OGLDisplayOutput.cpp in Sources */, AB796D2D15CDCBA200C59155 /* slot2_expMemory.cpp in Sources */, + AB47B52F18A45C35009A42AF /* xbrz.cpp in Sources */, AB796D2E15CDCBA200C59155 /* slot2_gbagame.cpp in Sources */, AB796D2F15CDCBA200C59155 /* slot2_guitarGrip.cpp in Sources */, AB796D3015CDCBA200C59155 /* slot2_mpcf.cpp in Sources */, diff --git a/desmume/src/cocoa/OGLDisplayOutput.cpp b/desmume/src/cocoa/OGLDisplayOutput.cpp new file mode 100644 index 000000000..020274f61 --- /dev/null +++ b/desmume/src/cocoa/OGLDisplayOutput.cpp @@ -0,0 +1,668 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . + */ + +#include "OGLDisplayOutput.h" +#include "cocoa_globals.h" +#include "utilities.h" +#include "../filter/videofilter.h" + + +// VERTEX SHADER FOR DISPLAY OUTPUT +static const char *vertexProgram_100 = {"\ + attribute vec2 inPosition; \n\ + attribute vec2 inTexCoord0; \n\ + \n\ + uniform vec2 viewSize; \n\ + uniform float scalar; \n\ + uniform float angleDegrees; \n\ + \n\ + varying vec2 vtxTexCoord; \n\ + \n\ + void main() \n\ + { \n\ + float angleRadians = radians(angleDegrees); \n\ + \n\ + mat2 projection = mat2( vec2(2.0/viewSize.x, 0.0), \n\ + vec2( 0.0, 2.0/viewSize.y)); \n\ + \n\ + mat2 rotation = mat2( vec2(cos(angleRadians), -sin(angleRadians)), \n\ + vec2(sin(angleRadians), cos(angleRadians))); \n\ + \n\ + mat2 scale = mat2( vec2(scalar, 0.0), \n\ + vec2( 0.0, scalar)); \n\ + \n\ + vtxTexCoord = inTexCoord0; \n\ + gl_Position = vec4(projection * rotation * scale * inPosition, 1.0, 1.0); \n\ + } \n\ +"}; + +// FRAGMENT SHADER FOR DISPLAY OUTPUT +static const char *fragmentProgram_100 = {"\ + varying vec2 vtxTexCoord; \n\ + uniform sampler2D tex; \n\ + \n\ + void main() \n\ + { \n\ + gl_FragColor = texture2D(tex, vtxTexCoord); \n\ + } \n\ +"}; + +enum OGLVertexAttributeID +{ + OGLVertexAttributeID_Position = 0, + OGLVertexAttributeID_TexCoord0 = 8 +}; + + +OGLVideoOutput::OGLVideoOutput() +{ + _gapScalar = 0.0f; + _normalWidth = GPU_DISPLAY_WIDTH; + _normalHeight = GPU_DISPLAY_HEIGHT*2.0 + (DS_DISPLAY_GAP*_gapScalar); + _displayMode = DS_DISPLAY_TYPE_DUAL; + _displayOrder = DS_DISPLAY_ORDER_MAIN_FIRST; + _displayOrientation = DS_DISPLAY_ORIENTATION_VERTICAL; + _rotation = 0.0f; + + _displayTexFilter = GL_NEAREST; + _glTexPixelFormat = GL_UNSIGNED_SHORT_1_5_5_5_REV; + _glTexBackWidth = GetNearestPositivePOT((uint32_t)_normalWidth); + _glTexBackHeight = GetNearestPositivePOT((uint32_t)_normalHeight); + _glTexBack = (GLvoid *)calloc(_glTexBackWidth * _glTexBackHeight, sizeof(uint16_t)); + + _vfSingle = new VideoFilter(GPU_DISPLAY_WIDTH, GPU_DISPLAY_HEIGHT, VideoFilterTypeID_None, 0); + _vfDual = new VideoFilter(GPU_DISPLAY_WIDTH, GPU_DISPLAY_HEIGHT*2, VideoFilterTypeID_None, 2); + _vf = _vfDual; + + UpdateVertices(); + UpdateTexCoords(1.0f, 2.0f); + _vtxBufferOffset = 0; + + // Set up initial vertex elements + vtxIndexBuffer[0] = 0; vtxIndexBuffer[1] = 1; vtxIndexBuffer[2] = 2; + vtxIndexBuffer[3] = 2; vtxIndexBuffer[4] = 3; vtxIndexBuffer[5] = 0; + + vtxIndexBuffer[6] = 4; vtxIndexBuffer[7] = 5; vtxIndexBuffer[8] = 6; + vtxIndexBuffer[9] = 6; vtxIndexBuffer[10] = 7; vtxIndexBuffer[11] = 4; +} + +OGLVideoOutput::~OGLVideoOutput() +{ + free(_glTexBack); + delete _vfSingle; + delete _vfDual; + _vf = NULL; +} + +void OGLVideoOutput::InitializeOGL() +{ + // Check the OpenGL capabilities for this renderer + std::set oglExtensionSet; + GetExtensionSetOGL(&oglExtensionSet); + + // Set up textures + glGenTextures(1, &this->_displayTexID); + glBindTexture(GL_TEXTURE_2D, this->_displayTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + // Set up shaders (but disable on PowerPC, since it doesn't seem to work there) +#if defined(__i386__) || defined(__x86_64__) + _isShaderSupported = IsExtensionPresent(oglExtensionSet, "GL_ARB_shader_objects") && + IsExtensionPresent(oglExtensionSet, "GL_ARB_vertex_shader") && + IsExtensionPresent(oglExtensionSet, "GL_ARB_fragment_shader") && + IsExtensionPresent(oglExtensionSet, "GL_ARB_vertex_program"); +#else + this->_isShaderSupported = false; +#endif + if (this->_isShaderSupported) + { + bool isShaderSetUp = SetupShadersOGL(vertexProgram_100, fragmentProgram_100); + if (isShaderSetUp) + { + glUseProgram(this->_shaderProgram); + + this->_uniformAngleDegrees = glGetUniformLocation(this->_shaderProgram, "angleDegrees"); + this->_uniformScalar = glGetUniformLocation(this->_shaderProgram, "scalar"); + this->_uniformViewSize = glGetUniformLocation(this->_shaderProgram, "viewSize"); + + glUniform1f(this->_uniformAngleDegrees, 0.0f); + glUniform1f(this->_uniformScalar, 1.0f); + glUniform2f(this->_uniformViewSize, GPU_DISPLAY_WIDTH, GPU_DISPLAY_HEIGHT*2.0 + (DS_DISPLAY_GAP*this->_gapScalar)); + } + else + { + this->_isShaderSupported = false; + } + } + + // Set up VBOs + glGenBuffersARB(1, &this->_vboVertexID); + glGenBuffersARB(1, &this->_vboTexCoordID); + glGenBuffersARB(1, &this->_vboElementID); + + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboVertexID); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLint) * (2 * 8), this->vtxBuffer, GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboTexCoordID); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat) * (2 * 8), this->texCoordBuffer, GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, this->_vboElementID); + glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLubyte) * 12, this->vtxIndexBuffer, GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + + // Set up VAO + glGenVertexArraysAPPLE(1, &this->_vaoMainStatesID); + glBindVertexArrayAPPLE(this->_vaoMainStatesID); + + if (this->_isShaderSupported) + { + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboVertexID); + glVertexAttribPointer(OGLVertexAttributeID_Position, 2, GL_INT, GL_FALSE, 0, 0); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboTexCoordID); + glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, 0, 0); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, this->_vboElementID); + + glEnableVertexAttribArray(OGLVertexAttributeID_Position); + glEnableVertexAttribArray(OGLVertexAttributeID_TexCoord0); + } + else + { + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboVertexID); + glVertexPointer(2, GL_INT, 0, 0); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboTexCoordID); + glTexCoordPointer(2, GL_FLOAT, 0, 0); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, this->_vboElementID); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + + glBindVertexArrayAPPLE(0); + + // Render State Setup (common to both shaders and fixed-function pipeline) + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_DITHER); + glDisable(GL_STENCIL_TEST); + + // Set up fixed-function pipeline render states. + if (!_isShaderSupported) + { + glDisable(GL_ALPHA_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glEnable(GL_TEXTURE_2D); + } + + // Set up clear attributes + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void OGLVideoOutput::TerminateOGL() +{ + glDeleteTextures(1, &this->_displayTexID); + + glDeleteVertexArraysAPPLE(1, &this->_vaoMainStatesID); + glDeleteBuffersARB(1, &this->_vboVertexID); + glDeleteBuffersARB(1, &this->_vboTexCoordID); + glDeleteBuffersARB(1, &this->_vboElementID); + + if (this->_isShaderSupported) + { + glUseProgram(0); + + glDetachShader(this->_shaderProgram, this->_vertexShaderID); + glDetachShader(this->_shaderProgram, this->_fragmentShaderID); + + glDeleteProgram(this->_shaderProgram); + glDeleteShader(this->_vertexShaderID); + glDeleteShader(this->_fragmentShaderID); + } +} + +int OGLVideoOutput::GetDisplayMode() +{ + return this->_displayMode; +} + +void OGLVideoOutput::SetDisplayMode(int dispMode) +{ + this->_displayMode = dispMode; + this->_vf = (dispMode == DS_DISPLAY_TYPE_DUAL) ? this->_vfDual : this->_vfSingle; + this->CalculateDisplayNormalSize(&this->_normalWidth, &this->_normalHeight); + this->UpdateVertices(); +} + +int OGLVideoOutput::GetDisplayOrientation() +{ + return this->_displayOrientation; +} + +void OGLVideoOutput::SetDisplayOrientation(int dispOrientation) +{ + this->_displayOrientation = dispOrientation; + this->CalculateDisplayNormalSize(&this->_normalWidth, &this->_normalHeight); + this->UpdateVertices(); +} + +GLfloat OGLVideoOutput::GetGapScalar() +{ + return this->_gapScalar; +} + +void OGLVideoOutput::SetGapScalar(GLfloat theScalar) +{ + this->_gapScalar = theScalar; + this->CalculateDisplayNormalSize(&this->_normalWidth, &this->_normalHeight); + this->UpdateVertices(); +} + +GLfloat OGLVideoOutput::GetRotation() +{ + return this->_rotation; +} + +void OGLVideoOutput::SetRotation(GLfloat theRotation) +{ + this->_rotation = theRotation; +} + +bool OGLVideoOutput::GetDisplayBilinear() +{ + return (this->_displayTexFilter == GL_LINEAR); +} + +void OGLVideoOutput::SetDisplayBilinearOGL(bool useBilinear) +{ + this->_displayTexFilter = (useBilinear) ? GL_LINEAR : GL_NEAREST; + + glBindTexture(GL_TEXTURE_2D, this->_displayTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, this->_displayTexFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, this->_displayTexFilter); + glBindTexture(GL_TEXTURE_2D, 0); +} + +int OGLVideoOutput::GetDisplayOrder() +{ + return this->_displayOrder; +} + +void OGLVideoOutput::SetDisplayOrder(int dispOrder) +{ + this->_displayOrder = dispOrder; + + if (this->_displayOrder == DS_DISPLAY_ORDER_MAIN_FIRST) + { + this->_vtxBufferOffset = 0; + } + else // dispOrder == DS_DISPLAY_ORDER_TOUCH_FIRST + { + this->_vtxBufferOffset = (2 * 8); + } +} + +void OGLVideoOutput::SetViewportSizeOGL(GLsizei w, GLsizei h) +{ + this->_viewportWidth = w; + this->_viewportHeight = h; + const CGSize checkSize = GetTransformedBounds(this->_normalWidth, this->_normalHeight, 1.0, this->_rotation); + const GLdouble s = GetMaxScalarInBounds(checkSize.width, checkSize.height, w, h); + + glViewport(0, 0, w, h); + + if (this->_isShaderSupported) + { + glUniform2f(this->_uniformViewSize, w, h); + glUniform1f(this->_uniformScalar, s); + } + else + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-w/2, -w/2 + w, -h/2, -h/2 + h, -1.0, 1.0); + glRotatef(CLOCKWISE_DEGREES(this->_rotation), 0.0f, 0.0f, 1.0f); + glScalef(s, s, 1.0f); + } +} + +void OGLVideoOutput::UpdateDisplayTransformationOGL() +{ + const CGSize checkSize = GetTransformedBounds(this->_normalWidth, this->_normalHeight, 1.0, this->_rotation); + const GLdouble s = GetMaxScalarInBounds(checkSize.width, checkSize.height, this->_viewportWidth, this->_viewportHeight); + + if (this->_isShaderSupported) + { + glUniform1f(this->_uniformAngleDegrees, this->_rotation); + glUniform1f(this->_uniformScalar, s); + } + else + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotatef(CLOCKWISE_DEGREES(this->_rotation), 0.0f, 0.0f, 1.0f); + glScalef(s, s, 1.0f); + } +} + +void OGLVideoOutput::RespondToVideoFilterChangeOGL(const VideoFilterTypeID videoFilterTypeID) +{ + this->_vfSingle->ChangeFilterByID(videoFilterTypeID); + this->_vfDual->ChangeFilterByID(videoFilterTypeID); + + const GLsizei vfDstWidth = this->_vf->GetDstWidth(); + const GLsizei vfDstHeight = (this->_displayMode == DS_DISPLAY_TYPE_DUAL) ? this->_vf->GetDstHeight() : this->_vf->GetDstHeight() * 2; + + size_t colorDepth = sizeof(uint32_t); + this->_glTexPixelFormat = GL_UNSIGNED_INT_8_8_8_8_REV; + + if (videoFilterTypeID == VideoFilterTypeID_None) + { + colorDepth = sizeof(uint16_t); + this->_glTexPixelFormat = GL_UNSIGNED_SHORT_1_5_5_5_REV; + } + + // Convert textures to Power-of-Two to support older GPUs + // Example: Radeon X1600M on the 2006 MacBook Pro + const GLsizei potW = GetNearestPositivePOT((uint32_t)vfDstWidth); + const GLsizei potH = GetNearestPositivePOT((uint32_t)vfDstHeight); + + if (this->_glTexBackWidth != potW || this->_glTexBackHeight != potH) + { + this->_glTexBackWidth = potW; + this->_glTexBackHeight = potH; + + free(this->_glTexBack); + this->_glTexBack = (GLvoid *)calloc((size_t)potW * (size_t)potH, colorDepth); + if (this->_glTexBack == NULL) + { + return; + } + } + + const GLfloat s = (GLfloat)vfDstWidth / (GLfloat)potW; + const GLfloat t = (GLfloat)vfDstHeight / (GLfloat)potH; + this->UpdateTexCoords(s, t); + + glBindTexture(GL_TEXTURE_2D, this->_displayTexID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)potW, (GLsizei)potH, 0, GL_BGRA, this->_glTexPixelFormat, this->_glTexBack); + glBindTexture(GL_TEXTURE_2D, 0); + this->UploadTexCoordsOGL(); +} + +void OGLVideoOutput::CalculateDisplayNormalSize(double *w, double *h) +{ + if (w == NULL || h == NULL) + { + return; + } + + if (this->_displayMode != DS_DISPLAY_TYPE_DUAL) + { + *w = GPU_DISPLAY_WIDTH; + *h = GPU_DISPLAY_HEIGHT; + return; + } + + if (this->_displayOrientation == DS_DISPLAY_ORIENTATION_VERTICAL) + { + *w = GPU_DISPLAY_WIDTH; + *h = GPU_DISPLAY_HEIGHT * 2.0 + (DS_DISPLAY_GAP * this->_gapScalar); + } + else + { + *w = GPU_DISPLAY_WIDTH * 2.0 + (DS_DISPLAY_GAP * this->_gapScalar); + *h = GPU_DISPLAY_HEIGHT; + } +} + +void OGLVideoOutput::GetExtensionSetOGL(std::set *oglExtensionSet) +{ + std::string oglExtensionString = std::string((const char *)glGetString(GL_EXTENSIONS)); + + size_t extStringStartLoc = 0; + size_t delimiterLoc = oglExtensionString.find_first_of(' ', extStringStartLoc); + while (delimiterLoc != std::string::npos) + { + std::string extensionName = oglExtensionString.substr(extStringStartLoc, delimiterLoc - extStringStartLoc); + oglExtensionSet->insert(extensionName); + + extStringStartLoc = delimiterLoc + 1; + delimiterLoc = oglExtensionString.find_first_of(' ', extStringStartLoc); + } + + if (extStringStartLoc - oglExtensionString.length() > 0) + { + std::string extensionName = oglExtensionString.substr(extStringStartLoc, oglExtensionString.length() - extStringStartLoc); + oglExtensionSet->insert(extensionName); + } +} + +bool OGLVideoOutput::IsExtensionPresent(const std::set &oglExtensionSet, const std::string &extensionName) const +{ + if (oglExtensionSet.size() == 0) + { + return false; + } + + return (oglExtensionSet.find(extensionName) != oglExtensionSet.end()); +} + +bool OGLVideoOutput::SetupShadersOGL(const char *vertexProgram, const char *fragmentProgram) +{ + bool result = false; + GLint shaderStatus = GL_TRUE; + + this->_vertexShaderID = glCreateShader(GL_VERTEX_SHADER); + if (this->_vertexShaderID == 0) + { + printf("OpenGL Error - Failed to create vertex shader."); + return result; + } + + glShaderSource(this->_vertexShaderID, 1, (const GLchar **)&vertexProgram, NULL); + glCompileShader(this->_vertexShaderID); + glGetShaderiv(this->_vertexShaderID, GL_COMPILE_STATUS, &shaderStatus); + if (shaderStatus == GL_FALSE) + { + glDeleteShader(this->_vertexShaderID); + printf("OpenGL Error - Failed to compile vertex shader."); + return result; + } + + this->_fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); + if (this->_fragmentShaderID == 0) + { + glDeleteShader(this->_vertexShaderID); + printf("OpenGL Error - Failed to create fragment shader."); + return result; + } + + glShaderSource(this->_fragmentShaderID, 1, (const GLchar **)&fragmentProgram, NULL); + glCompileShader(this->_fragmentShaderID); + glGetShaderiv(this->_fragmentShaderID, GL_COMPILE_STATUS, &shaderStatus); + if (shaderStatus == GL_FALSE) + { + glDeleteShader(this->_vertexShaderID); + glDeleteShader(this->_fragmentShaderID); + printf("OpenGL Error - Failed to compile fragment shader."); + return result; + } + + this->_shaderProgram = glCreateProgram(); + if (this->_shaderProgram == 0) + { + glDeleteShader(this->_vertexShaderID); + glDeleteShader(this->_fragmentShaderID); + printf("OpenGL Error - Failed to create shader program."); + return result; + } + + glAttachShader(this->_shaderProgram, this->_vertexShaderID); + glAttachShader(this->_shaderProgram, this->_fragmentShaderID); + + this->SetupShaderIO_OGL(); + + glLinkProgram(this->_shaderProgram); + glGetProgramiv(this->_shaderProgram, GL_LINK_STATUS, &shaderStatus); + if (shaderStatus == GL_FALSE) + { + glDeleteProgram(this->_shaderProgram); + glDeleteShader(this->_vertexShaderID); + glDeleteShader(this->_fragmentShaderID); + printf("OpenGL Error - Failed to link shader program."); + return result; + } + + glValidateProgram(this->_shaderProgram); + + result = true; + return result; +} + +void OGLVideoOutput::SetupShaderIO_OGL() +{ + glBindAttribLocation(this->_shaderProgram, OGLVertexAttributeID_Position, "inPosition"); + glBindAttribLocation(this->_shaderProgram, OGLVertexAttributeID_TexCoord0, "inTexCoord0"); +} + +void OGLVideoOutput::UploadVerticesOGL() +{ + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboVertexID); + glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(GLint) * (2 * 8), this->vtxBuffer + this->_vtxBufferOffset); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); +} + +void OGLVideoOutput::UploadTexCoordsOGL() +{ + glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->_vboTexCoordID); + glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(GLfloat) * (2 * 8), this->texCoordBuffer); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); +} + +void OGLVideoOutput::UploadDisplayTextureOGL(const GLvoid *textureData, GLsizei texWidth, GLsizei texHeight) +{ + if (textureData == NULL) + { + return; + } + + const GLint lineOffset = (this->_displayMode == DS_DISPLAY_TYPE_TOUCH) ? texHeight : 0; + + glBindTexture(GL_TEXTURE_2D, this->_displayTexID); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, lineOffset, texWidth, texHeight, GL_RGBA, this->_glTexPixelFormat, textureData); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void OGLVideoOutput::UpdateVertices() +{ + const GLfloat w = GPU_DISPLAY_WIDTH; + const GLfloat h = GPU_DISPLAY_HEIGHT; + const GLfloat gap = DS_DISPLAY_GAP * this->_gapScalar / 2.0; + + if (this->_displayMode == DS_DISPLAY_TYPE_DUAL) + { + // displayOrder == DS_DISPLAY_ORDER_MAIN_FIRST + if (this->_displayOrientation == DS_DISPLAY_ORIENTATION_VERTICAL) + { + vtxBuffer[0] = -w/2; vtxBuffer[1] = h+gap; // Top display, top left + vtxBuffer[2] = w/2; vtxBuffer[3] = h+gap; // Top display, top right + vtxBuffer[4] = w/2; vtxBuffer[5] = gap; // Top display, bottom right + vtxBuffer[6] = -w/2; vtxBuffer[7] = gap; // Top display, bottom left + + vtxBuffer[8] = -w/2; vtxBuffer[9] = -gap; // Bottom display, top left + vtxBuffer[10] = w/2; vtxBuffer[11] = -gap; // Bottom display, top right + vtxBuffer[12] = w/2; vtxBuffer[13] = -(h+gap); // Bottom display, bottom right + vtxBuffer[14] = -w/2; vtxBuffer[15] = -(h+gap); // Bottom display, bottom left + } + else // displayOrientationID == DS_DISPLAY_ORIENTATION_HORIZONTAL + { + vtxBuffer[0] = -(w+gap); vtxBuffer[1] = h/2; // Left display, top left + vtxBuffer[2] = -gap; vtxBuffer[3] = h/2; // Left display, top right + vtxBuffer[4] = -gap; vtxBuffer[5] = -h/2; // Left display, bottom right + vtxBuffer[6] = -(w+gap); vtxBuffer[7] = -h/2; // Left display, bottom left + + vtxBuffer[8] = gap; vtxBuffer[9] = h/2; // Right display, top left + vtxBuffer[10] = w+gap; vtxBuffer[11] = h/2; // Right display, top right + vtxBuffer[12] = w+gap; vtxBuffer[13] = -h/2; // Right display, bottom right + vtxBuffer[14] = gap; vtxBuffer[15] = -h/2; // Right display, bottom left + } + + // displayOrder == DS_DISPLAY_ORDER_TOUCH_FIRST + memcpy(vtxBuffer + (2 * 8), vtxBuffer + (1 * 8), sizeof(GLint) * (1 * 8)); + memcpy(vtxBuffer + (3 * 8), vtxBuffer + (0 * 8), sizeof(GLint) * (1 * 8)); + } + else // displayModeID == DS_DISPLAY_TYPE_MAIN || displayModeID == DS_DISPLAY_TYPE_TOUCH + { + vtxBuffer[0] = -w/2; vtxBuffer[1] = h/2; // First display, top left + vtxBuffer[2] = w/2; vtxBuffer[3] = h/2; // First display, top right + vtxBuffer[4] = w/2; vtxBuffer[5] = -h/2; // First display, bottom right + vtxBuffer[6] = -w/2; vtxBuffer[7] = -h/2; // First display, bottom left + + memcpy(vtxBuffer + (1 * 8), vtxBuffer + (0 * 8), sizeof(GLint) * (1 * 8)); // Second display + memcpy(vtxBuffer + (2 * 8), vtxBuffer + (0 * 8), sizeof(GLint) * (2 * 8)); // Second display + } +} + +void OGLVideoOutput::UpdateTexCoords(GLfloat s, GLfloat t) +{ + texCoordBuffer[0] = 0.0f; texCoordBuffer[1] = 0.0f; + texCoordBuffer[2] = s; texCoordBuffer[3] = 0.0f; + texCoordBuffer[4] = s; texCoordBuffer[5] = t/2.0f; + texCoordBuffer[6] = 0.0f; texCoordBuffer[7] = t/2.0f; + + texCoordBuffer[8] = 0.0f; texCoordBuffer[9] = t/2.0f; + texCoordBuffer[10] = s; texCoordBuffer[11] = t/2.0f; + texCoordBuffer[12] = s; texCoordBuffer[13] = t; + texCoordBuffer[14] = 0.0f; texCoordBuffer[15] = t; +} + +void OGLVideoOutput::PrerenderOGL(const GLvoid *textureData, GLsizei texWidth, GLsizei texHeight) +{ + uint32_t *vfTextureData = (uint32_t *)textureData; + + if (this->_vf->GetTypeID() != VideoFilterTypeID_None) + { + RGB555ToRGBA8888Buffer((const uint16_t *)textureData, (uint32_t *)this->_vf->GetSrcBufferPtr(), texWidth * texHeight); + texWidth = this->_vf->GetDstWidth(); + texHeight = this->_vf->GetDstHeight(); + vfTextureData = this->_vf->RunFilter(); + } + + this->UploadDisplayTextureOGL(vfTextureData, texWidth, texHeight); +} + +void OGLVideoOutput::RenderOGL() +{ + // Enable vertex attributes + glBindVertexArrayAPPLE(this->_vaoMainStatesID); + + const GLsizei vtxElementCount = (this->_displayMode == DS_DISPLAY_TYPE_DUAL) ? 12 : 6; + const GLubyte *elementPointer = !(this->_displayMode == DS_DISPLAY_TYPE_TOUCH) ? 0 : (GLubyte *)(vtxElementCount * sizeof(GLubyte)); + + glClear(GL_COLOR_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, this->_displayTexID); + glDrawElements(GL_TRIANGLES, vtxElementCount, GL_UNSIGNED_BYTE, elementPointer); + glBindTexture(GL_TEXTURE_2D, 0); + + // Disable vertex attributes + glBindVertexArrayAPPLE(0); +} diff --git a/desmume/src/cocoa/OGLDisplayOutput.h b/desmume/src/cocoa/OGLDisplayOutput.h new file mode 100644 index 000000000..d0b25e3db --- /dev/null +++ b/desmume/src/cocoa/OGLDisplayOutput.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . + */ + +#ifndef _OGLDISPLAYOUTPUT_H_ +#define _OGLDISPLAYOUTPUT_H_ + +#if defined(__APPLE__) + #include + #include +#endif + +#include +#include +#include "../filter/videofilter.h" + + +class OGLVideoOutput +{ +protected: + VideoFilter *_vfSingle; + VideoFilter *_vfDual; + VideoFilter *_vf; + bool _isShaderSupported; + double _normalWidth; + double _normalHeight; + int _displayMode; + int _displayOrder; + int _displayOrientation; + + GLint _displayTexFilter; + GLsizei _viewportWidth; + GLsizei _viewportHeight; + GLfloat _gapScalar; + GLfloat _rotation; + + GLenum _glTexPixelFormat; + GLvoid *_glTexBack; + GLsizei _glTexBackWidth; + GLsizei _glTexBackHeight; + + GLuint _displayTexID; + GLuint _vboVertexID; + GLuint _vboTexCoordID; + GLuint _vboElementID; + GLuint _vaoMainStatesID; + GLuint _vertexShaderID; + GLuint _fragmentShaderID; + GLuint _shaderProgram; + + GLint _uniformAngleDegrees; + GLint _uniformScalar; + GLint _uniformViewSize; + + GLint vtxBuffer[4 * 8]; + GLfloat texCoordBuffer[2 * 8]; + GLubyte vtxIndexBuffer[12]; + size_t _vtxBufferOffset; + + void GetExtensionSetOGL(std::set *oglExtensionSet); + bool IsExtensionPresent(const std::set &oglExtensionSet, const std::string &extensionName) const; + bool SetupShadersOGL(const char *vertexProgram, const char *fragmentProgram); + void SetupShaderIO_OGL(); + +public: + OGLVideoOutput(); + ~OGLVideoOutput(); + + virtual void InitializeOGL(); + virtual void TerminateOGL(); + + virtual void UploadVerticesOGL(); + virtual void UploadTexCoordsOGL(); + virtual void UploadDisplayTextureOGL(const GLvoid *textureData, GLsizei texWidth, GLsizei texHeight); + + virtual void PrerenderOGL(const GLvoid *textureData, GLsizei texWidth, GLsizei texHeight); + virtual void RenderOGL(); + virtual void SetViewportSizeOGL(GLsizei w, GLsizei h); + virtual void UpdateDisplayTransformationOGL(); + virtual void RespondToVideoFilterChangeOGL(const VideoFilterTypeID videoFilterTypeID); + + void CalculateDisplayNormalSize(double *w, double *h); + void UpdateVertices(); + void UpdateTexCoords(GLfloat s, GLfloat t); + + int GetDisplayMode(); + void SetDisplayMode(int dispMode); + int GetDisplayOrientation(); + void SetDisplayOrientation(int dispOrientation); + GLfloat GetGapScalar(); + void SetGapScalar(GLfloat theScalar); + GLfloat GetRotation(); + void SetRotation(GLfloat theRotation); + bool GetDisplayBilinear(); + void SetDisplayBilinearOGL(bool useBilinear); + int GetDisplayOrder(); + void SetDisplayOrder(int dispOrder); +}; + +#endif // _OGLDISPLAYOUTPUT_H_ diff --git a/desmume/src/cocoa/cocoa_globals.h b/desmume/src/cocoa/cocoa_globals.h index b2e54790a..da2856091 100644 --- a/desmume/src/cocoa/cocoa_globals.h +++ b/desmume/src/cocoa/cocoa_globals.h @@ -428,8 +428,6 @@ enum MESSAGE_CHANGE_DISPLAY_ORIENTATION, MESSAGE_CHANGE_DISPLAY_ORDER, MESSAGE_CHANGE_DISPLAY_GAP, - MESSAGE_CHANGE_BILINEAR_OUTPUT, - MESSAGE_CHANGE_VERTICAL_SYNC, MESSAGE_CHANGE_VIDEO_FILTER, MESSAGE_SET_RENDER3D_METHOD, MESSAGE_SET_RENDER3D_HIGH_PRECISION_COLOR_INTERPOLATION, diff --git a/desmume/src/cocoa/cocoa_output.h b/desmume/src/cocoa/cocoa_output.h index 1a09c199c..d68250030 100644 --- a/desmume/src/cocoa/cocoa_output.h +++ b/desmume/src/cocoa/cocoa_output.h @@ -120,16 +120,13 @@ typedef struct @required - (void) doDisplayModeChanged:(NSInteger)displayModeID; -@property (assign) BOOL isHudEnabled; -@property (assign) BOOL isHudEditingModeEnabled; - @end @protocol CocoaDSDisplayVideoDelegate @required - (void) doInitVideoOutput:(NSDictionary *)properties; -- (void) doProcessVideoFrame:(const void *)videoFrameData displayMode:(const NSInteger)displayModeID width:(const NSInteger)frameWidth height:(const NSInteger)frameHeight; +- (void) doProcessVideoFrame:(const void *)videoFrameData displayMode:(const NSInteger)frameDisplayMode width:(const NSInteger)frameWidth height:(const NSInteger)frameHeight; @optional - (void) doResizeView:(NSRect)rect; @@ -140,7 +137,7 @@ typedef struct - (void) doDisplayGapChanged:(float)displayGapScalar; - (void) doBilinearOutputChanged:(BOOL)useBilinear; - (void) doVerticalSyncChanged:(BOOL)useVerticalSync; -- (void) doVideoFilterChanged:(NSInteger)videoFilterTypeID frameSize:(NSSize)videoFilterDestSize; +- (void) doVideoFilterChanged:(NSInteger)videoFilterTypeID; @end @@ -151,7 +148,6 @@ typedef struct NSInteger displayMode; NSSize frameSize; - OSSpinLock spinlockDelegate; OSSpinLock spinlockDisplayType; } @@ -174,25 +170,15 @@ typedef struct @interface CocoaDSDisplayVideo : CocoaDSDisplay { - CocoaVideoFilter *vf; - id videoDelegate; - NSInteger lastDisplayMode; - - OSSpinLock spinlockVideoFilterType; - OSSpinLock spinlockVFBuffers; + } -@property (retain) id delegate; -@property (assign) CocoaVideoFilter *vf; - - (void) handleResizeView:(NSData *)rectData; - (void) handleTransformView:(NSData *)transformData; - (void) handleRedrawView; - (void) handleChangeDisplayOrientation:(NSData *)displayOrientationIdData; - (void) handleChangeDisplayOrder:(NSData *)displayOrderIdData; - (void) handleChangeDisplayGap:(NSData *)displayGapScalarData; -- (void) handleChangeBilinearOutput:(NSData *)bilinearStateData; -- (void) handleChangeVerticalSync:(NSData *)verticalSyncStateData; - (void) handleChangeVideoFilter:(NSData *)videoFilterTypeIdData; @end diff --git a/desmume/src/cocoa/cocoa_output.mm b/desmume/src/cocoa/cocoa_output.mm index a3797753b..c9663e34a 100644 --- a/desmume/src/cocoa/cocoa_output.mm +++ b/desmume/src/cocoa/cocoa_output.mm @@ -513,7 +513,7 @@ @implementation CocoaDSDisplay -@dynamic delegate; +@synthesize delegate; @dynamic displayMode; @dynamic frameSize; @@ -525,8 +525,7 @@ { return self; } - - spinlockDelegate = OS_SPINLOCK_INIT; + spinlockDisplayType = OS_SPINLOCK_INIT; delegate = nil; @@ -541,41 +540,11 @@ - (void)dealloc { - self.delegate = nil; + [self setDelegate:nil]; [super dealloc]; } -- (void) setDelegate:(id )theDelegate -{ - OSSpinLockLock(&spinlockDelegate); - - if (theDelegate == delegate) - { - OSSpinLockUnlock(&spinlockDelegate); - return; - } - - if (theDelegate != nil) - { - [theDelegate retain]; - } - - [delegate release]; - delegate = theDelegate; - - OSSpinLockUnlock(&spinlockDelegate); -} - -- (id ) delegate -{ - OSSpinLockLock(&spinlockDelegate); - id theDelegate = delegate; - OSSpinLockUnlock(&spinlockDelegate); - - return theDelegate; -} - - (void) setDisplayMode:(NSInteger)displayModeID { NSString *newDispString = nil; @@ -712,7 +681,7 @@ } const NSInteger displayModeID = *(NSInteger *)[displayModeData bytes]; - self.displayMode = displayModeID; + [self setDisplayMode:displayModeID]; [delegate doDisplayModeChanged:displayModeID]; } @@ -850,8 +819,6 @@ @implementation CocoaDSDisplayVideo -@synthesize vf; - - (id)init { self = [super init]; @@ -860,21 +827,6 @@ return self; } - videoDelegate = nil; - lastDisplayMode = DS_DISPLAY_TYPE_DUAL; - - spinlockVideoFilterType = OS_SPINLOCK_INIT; - spinlockVFBuffers = OS_SPINLOCK_INIT; - - if ([[NSProcessInfo processInfo] activeProcessorCount] >= 2) - { - vf = [[CocoaVideoFilter alloc] initWithSize:frameSize typeID:VideoFilterTypeID_None numberThreads:2]; - } - else - { - vf = [[CocoaVideoFilter alloc] initWithSize:frameSize typeID:VideoFilterTypeID_None numberThreads:0]; - } - [property setValue:[NSNumber numberWithInteger:(NSInteger)VideoFilterTypeID_None] forKey:@"videoFilterType"]; [property setValue:[CocoaVideoFilter typeStringByID:VideoFilterTypeID_None] forKey:@"videoFilterTypeString"]; @@ -883,68 +835,13 @@ - (void)dealloc { - [vf release]; - [super dealloc]; } -- (void) setDelegate:(id )theDelegate -{ - OSSpinLockLock(&spinlockDelegate); - - if (theDelegate == videoDelegate) - { - OSSpinLockUnlock(&spinlockDelegate); - return; - } - - if (theDelegate != nil) - { - [theDelegate retain]; - } - - [videoDelegate release]; - videoDelegate = theDelegate; - - OSSpinLockUnlock(&spinlockDelegate); - - [super setDelegate:theDelegate]; -} - -- (id ) delegate -{ - OSSpinLockLock(&spinlockDelegate); - id theDelegate = videoDelegate; - OSSpinLockUnlock(&spinlockDelegate); - - return theDelegate; -} - -- (void) setVfType:(NSInteger)videoFilterTypeID -{ - OSSpinLockLock(&spinlockVFBuffers); - [vf changeFilter:(VideoFilterTypeID)videoFilterTypeID]; - OSSpinLockUnlock(&spinlockVFBuffers); - - OSSpinLockLock(&spinlockVideoFilterType); - [property setValue:[NSNumber numberWithInteger:videoFilterTypeID] forKey:@"videoFilterType"]; - [property setValue:NSLocalizedString([vf typeString], nil) forKey:@"videoFilterTypeString"]; - OSSpinLockUnlock(&spinlockVideoFilterType); -} - -- (NSInteger) vfType -{ - OSSpinLockLock(&spinlockVideoFilterType); - NSInteger theType = [(NSNumber *)[property valueForKey:@"videoFilterType"] integerValue]; - OSSpinLockUnlock(&spinlockVideoFilterType); - - return theType; -} - - (void) runThread:(id)object { NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; - [videoDelegate doInitVideoOutput:self.property]; + [(id)delegate doInitVideoOutput:self.property]; [tempPool release]; [super runThread:object]; @@ -985,14 +882,6 @@ [self handleChangeDisplayGap:[messageComponents objectAtIndex:0]]; break; - case MESSAGE_CHANGE_BILINEAR_OUTPUT: - [self handleChangeBilinearOutput:[messageComponents objectAtIndex:0]]; - break; - - case MESSAGE_CHANGE_VERTICAL_SYNC: - [self handleChangeVerticalSync:[messageComponents objectAtIndex:0]]; - break; - case MESSAGE_CHANGE_VIDEO_FILTER: [self handleChangeVideoFilter:[messageComponents objectAtIndex:0]]; break; @@ -1011,134 +900,110 @@ } const DisplaySrcPixelAttributes attr = *(DisplaySrcPixelAttributes *)[attributesData bytes]; - const NSInteger displayModeID = attr.displayModeID; + const NSInteger frameDisplayMode = attr.displayModeID; + const NSInteger frameWidth = attr.width; + const NSInteger frameHeight = attr.height; - // Tell the video delegate to process the video frame with our copied GPU data. - OSSpinLockLock(&spinlockVFBuffers); - - if (lastDisplayMode != displayModeID) - { - const NSSize newSrcSize = NSMakeSize((CGFloat)attr.width, (CGFloat)attr.height); - [vf setSourceSize:newSrcSize]; - lastDisplayMode = displayModeID; - } - - const NSInteger destWidth = (NSInteger)[vf destSize].width; - const NSInteger destHeight = (NSInteger)[vf destSize].height; - - if ([vf typeID] == VideoFilterTypeID_None) - { - [videoDelegate doProcessVideoFrame:[mainData bytes] displayMode:displayModeID width:destWidth height:destHeight]; - } - else - { - RGB555ToRGBA8888Buffer((const uint16_t *)[mainData bytes], (uint32_t *)[vf srcBufferPtr], [mainData length] / sizeof(UInt16)); - const UInt32 *vfDestBuffer = [vf runFilter]; - [videoDelegate doProcessVideoFrame:vfDestBuffer displayMode:displayModeID width:destWidth height:destHeight]; - } - - OSSpinLockUnlock(&spinlockVFBuffers); - + [(id)delegate doProcessVideoFrame:[mainData bytes] displayMode:frameDisplayMode width:frameWidth height:frameHeight]; [super handleEmuFrameProcessed:mainData attributes:attributesData]; } - (void) handleResizeView:(NSData *)rectData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doResizeView:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doResizeView:)]) { return; } const NSRect resizeRect = *(NSRect *)[rectData bytes]; - [videoDelegate doResizeView:resizeRect]; + [(id)delegate doResizeView:resizeRect]; } - (void) handleTransformView:(NSData *)transformData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doTransformView:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doTransformView:)]) { return; } - [videoDelegate doTransformView:(DisplayOutputTransformData *)[transformData bytes]]; + [(id)delegate doTransformView:(DisplayOutputTransformData *)[transformData bytes]]; } - (void) handleRedrawView { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doRedraw)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doRedraw)]) { return; } - [videoDelegate doRedraw]; + [(id)delegate doRedraw]; } - (void) handleChangeDisplayOrientation:(NSData *)displayOrientationIdData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doDisplayOrientationChanged:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doDisplayOrientationChanged:)]) { return; } const NSInteger theOrientation = *(NSInteger *)[displayOrientationIdData bytes]; - [videoDelegate doDisplayOrientationChanged:theOrientation]; + [(id)delegate doDisplayOrientationChanged:theOrientation]; } - (void) handleChangeDisplayOrder:(NSData *)displayOrderIdData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doDisplayOrderChanged:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doDisplayOrderChanged:)]) { return; } const NSInteger theOrder = *(NSInteger *)[displayOrderIdData bytes]; - [videoDelegate doDisplayOrderChanged:theOrder]; + [(id)delegate doDisplayOrderChanged:theOrder]; } - (void) handleChangeDisplayGap:(NSData *)displayGapScalarData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doDisplayGapChanged:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doDisplayGapChanged:)]) { return; } const float gapScalar = *(float *)[displayGapScalarData bytes]; - [videoDelegate doDisplayGapChanged:gapScalar]; + [(id)delegate doDisplayGapChanged:gapScalar]; } - +/* - (void) handleChangeBilinearOutput:(NSData *)bilinearStateData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doBilinearOutputChanged:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doBilinearOutputChanged:)]) { return; } const BOOL theState = *(BOOL *)[bilinearStateData bytes]; - [videoDelegate doBilinearOutputChanged:theState]; + [(id)delegate doBilinearOutputChanged:theState]; [self handleEmuFrameProcessed:self.frameData attributes:self.frameAttributesData]; } - (void) handleChangeVerticalSync:(NSData *)verticalSyncStateData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doVerticalSyncChanged:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doVerticalSyncChanged:)]) { return; } const BOOL theState = *(BOOL *)[verticalSyncStateData bytes]; - [videoDelegate doVerticalSyncChanged:theState]; + [(id)delegate doVerticalSyncChanged:theState]; } - +*/ - (void) handleChangeVideoFilter:(NSData *)videoFilterTypeIdData { - if (videoDelegate == nil || ![videoDelegate respondsToSelector:@selector(doVideoFilterChanged:frameSize:)]) + if (delegate == nil || ![delegate respondsToSelector:@selector(doVideoFilterChanged:)]) { return; } const NSInteger theType = *(NSInteger *)[videoFilterTypeIdData bytes]; - [self setVfType:theType]; - [videoDelegate doVideoFilterChanged:theType frameSize:[vf destSize]]; + [(id)delegate doVideoFilterChanged:theType]; [self handleEmuFrameProcessed:self.frameData attributes:self.frameAttributesData]; } diff --git a/desmume/src/cocoa/translations/English.lproj/MainMenu.strings b/desmume/src/cocoa/translations/English.lproj/MainMenu.strings index b226c6ab8..599d9e4d9 100644 Binary files a/desmume/src/cocoa/translations/English.lproj/MainMenu.strings and b/desmume/src/cocoa/translations/English.lproj/MainMenu.strings differ diff --git a/desmume/src/cocoa/translations/English.lproj/MainMenu.xib b/desmume/src/cocoa/translations/English.lproj/MainMenu.xib index 3bf17437e..32f9628e8 100644 --- a/desmume/src/cocoa/translations/English.lproj/MainMenu.xib +++ b/desmume/src/cocoa/translations/English.lproj/MainMenu.xib @@ -12,7 +12,6 @@ YES - @@ -1343,6 +1342,42 @@ 18 + + + 2xBRZ + + 2147483647 + + + 19 + + + + 3xBRZ + + 2147483647 + + + 20 + + + + 4xBRZ + + 2147483647 + + + 21 + + + + 5xBRZ + + 2147483647 + + + 22 + 2xSaI @@ -8487,8 +8522,9 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 NSWindow + {1.7976931348623157e+308, 1.7976931348623157e+308} - + 256 YES @@ -8581,7 +8617,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {300, 108} - _NS:122 {{0, 0}, {1440, 878}} @@ -8599,7 +8634,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {1.7976931348623157e+308, 1.7976931348623157e+308} - + 256 YES @@ -8618,7 +8653,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{18, 14}, {190, 126}} - YES NO 4 @@ -8892,7 +8926,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{130, 18}, {224, 21}} - YES 612368448 @@ -8913,7 +8946,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{476, 11}, {96, 32}} - YES 67108864 @@ -8935,7 +8967,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{128, 47}, {348, 26}} - YES -2080112384 @@ -8958,7 +8989,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{479, 56}, {90, 17}} - YES 68157504 @@ -9037,7 +9067,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{356, 11}, {124, 32}} - YES 67108864 @@ -9057,12 +9086,10 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{1, 1}, {584, 150}} - {{17, 56}, {586, 166}} - {0, 0} 67108864 @@ -9086,7 +9113,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{510, 12}, {96, 32}} - 1 YES @@ -9109,7 +9135,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{414, 12}, {96, 32}} - YES 67108864 @@ -9131,7 +9156,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{17, 230}, {586, 17}} - YES 70254657 @@ -9148,8 +9172,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {620, 267} - - {{0, 0}, {1920, 1178}} {1.7976931348623157e+308, 1.7976931348623157e+308} @@ -15005,7 +15027,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {1.7976931348623157e+308, 1.7976931348623157e+308} - + 256 YES @@ -15014,7 +15036,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{14, 12}, {168, 32}} - YES 67108864 @@ -15036,7 +15057,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{181, 22}, {105, 17}} - YES 68157504 @@ -15055,7 +15075,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 12 {{12, 51}, {616, 5}} - {0, 0} 67108864 @@ -15078,7 +15097,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{288, 22}, {330, 17}} - YES 70254657 @@ -15108,7 +15126,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{18, 14}, {260, 128}} - YES NO 5 @@ -15394,7 +15411,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{85, 15}, {409, 21}} - YES 78643265 @@ -15415,7 +15431,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{496, 8}, {96, 32}} - YES 67108864 @@ -15437,7 +15452,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{15, 150}, {482, 28}} - YES 67108864 @@ -15459,7 +15473,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{496, 146}, {96, 32}} - YES 67108864 @@ -15479,12 +15492,10 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{1, 1}, {604, 188}} - {{17, 140}, {606, 204}} - {0, 0} 67108864 @@ -15530,7 +15541,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{18, 16}, {30, 30}} - YES 134217728 @@ -15549,7 +15559,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 265 {{50, 18}, {168, 28}} - YES 69206017 @@ -15568,7 +15577,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 1292 {{18, 14}, {32, 32}} - 28682 100 @@ -15577,7 +15585,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{220, 17}, {201, 26}} - YES -2076180416 @@ -15685,12 +15692,10 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{1, 1}, {604, 56}} - {{17, 64}, {606, 72}} - {0, 0} 67108864 @@ -15711,8 +15716,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {640, 355} - - {{0, 0}, {1920, 1178}} {1.7976931348623157e+308, 1.7976931348623157e+308} @@ -15730,7 +15733,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {1.7976931348623157e+308, 1.7976931348623157e+308} - + 256 YES @@ -15749,7 +15752,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 256 {188, 304} - _NS:1843 YES NO @@ -15759,7 +15761,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 256 {188, 17} - @@ -15817,7 +15818,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{1, 17}, {188, 304}} - _NS:1841 @@ -15829,7 +15829,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 -2147483392 {{224, 17}, {15, 102}} - _NS:1860 NO @@ -15841,7 +15840,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 -2147483392 {{1, 306}, {188.95703125, 15}} - _NS:1862 NO 1 @@ -15858,7 +15856,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{1, 0}, {188, 17}} - @@ -15867,7 +15864,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{20, 106}, {190, 322}} - _NS:1839 133682 @@ -15895,20 +15891,17 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{3, 4}, {400, 320}} - _NS:1109 NSView {{1, 1}, {406, 326}} - _NS:21 {{215, 102}, {408, 328}} - _NS:18 {0, 0} @@ -15933,7 +15926,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{530, 12}, {96, 32}} - _NS:610 YES @@ -15967,7 +15959,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 {{15, 14}, {482, 42}} - _NS:3939 YES @@ -15987,13 +15978,11 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{1, 1}, {512, 66}} - _NS:21 {{17, 16}, {514, 82}} - _NS:18 {0, 0} @@ -16015,8 +16004,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {640, 448} - - _NS:122 {{0, 0}, {1920, 1178}} @@ -19884,7 +19871,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 279 2 - {{1004, 215}, {204, 521}} + {{1004, 135}, {204, 601}} -461896704 Set Video Output NSPanel @@ -19930,12 +19917,12 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 - {{18, 14}, {129, 378}} + {{18, 14}, {132, 458}} YES NO - 19 + 23 1 YES @@ -20169,6 +20156,62 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 67108864 131072 + 2xBRZ + + + 19 + 1211912448 + 0 + + + 400 + 75 + + + 1140850688 + 131072 + 3xBRZ + + + 20 + 1211912448 + 0 + + + 400 + 75 + + + 67108864 + 131072 + 4xBRZ + + + 21 + 1211912448 + 0 + + + 400 + 75 + + + 67108864 + 131072 + 5xBRZ + + + 22 + 1211912448 + 0 + + + 400 + 75 + + + 1140850688 + 131072 2xSaI @@ -20180,8 +20223,8 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 400 75 - - 1140850688 + + 67108864 131072 Super 2xSaI @@ -20194,7 +20237,7 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 400 75 - + 67108864 131072 Super Eagle @@ -20208,7 +20251,7 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 400 75 - + 67108864 131072 Scanline @@ -20222,8 +20265,8 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 400 75 - - 1140850688 + + 67108864 131072 Bilinear @@ -20236,7 +20279,7 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 400 75 - + 67108864 131072 Nearest 2x @@ -20250,7 +20293,7 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 400 75 - + 67108864 131072 Nearest 1.5x @@ -20259,62 +20302,6 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 12 1211912448 0 - - - 400 - 75 - - - 67108864 - 131072 - Nearest+ 1.5x - - - 13 - 1211912448 - 0 - - - 400 - 75 - - - 67108864 - 131072 - EPX - - - 14 - 1211912448 - 0 - - - 400 - 75 - - - 67108864 - 131072 - EPX+ - - - 15 - 1211912448 - 0 - - - 400 - 75 - - - 67108864 - 131072 - EPX 1.5x - - - 16 - 1211912448 - 0 12779520 @@ -20354,6 +20341,58 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 75 + 67108864 + 131072 + Nearest+ 1.5x + + + 13 + 1211912448 + 0 + + 400 + 75 + + + 67108864 + 131072 + EPX + + + 14 + 1211912448 + 0 + + 400 + 75 + + + 67108864 + 131072 + EPX+ + + + 15 + 1211912448 + 0 + + 400 + 75 + + + 67108864 + 131072 + EPX 1.5x + + + 16 + 1211912448 + 0 + + 400 + 75 + + 67108864 131072 EPX+ 1.5x @@ -20367,7 +20406,7 @@ QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA 75 - {129, 18} + {132, 18} {4, 2} 1151868928 NSActionCell @@ -20482,12 +20521,12 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 - {{1, 1}, {168, 402}} + {{1, 1}, {168, 482}} - {{17, 41}, {170, 418}} + {{17, 41}, {170, 498}} {0, 0} @@ -20511,7 +20550,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 - {{17, 485}, {169, 18}} + {{17, 565}, {169, 18}} YES @@ -20535,7 +20574,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 268 - {{17, 465}, {135, 18}} + {{17, 545}, {135, 18}} YES @@ -20557,7 +20596,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 NO - {204, 521} + {204, 601} {{0, 0}, {1440, 878}} @@ -38037,6 +38076,38 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 9119 + + + changeVideoFilter: + + + + 9130 + + + + changeVideoFilter: + + + + 9132 + + + + changeVideoFilter: + + + + 9134 + + + + changeVideoFilter: + + + + 9136 + @@ -41397,7 +41468,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 YES - @@ -41415,6 +41485,11 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 + + + + + @@ -41428,11 +41503,6 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 - - 3665 - - - 3664 @@ -49382,6 +49452,10 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 + + + + @@ -51284,6 +51358,51 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 + + 9124 + + + + + 9125 + + + + + 9126 + + + + + 9127 + + + + + 3665 + + + + + 9129 + + + + + 9131 + + + + + 9133 + + + + + 9135 + + + @@ -51704,6 +51823,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 3523.IBPluginDependency 3644.IBPluginDependency 3648.IBPluginDependency + 3648.IBViewBoundsToFrameTransform 3649.IBPluginDependency 3650.IBPluginDependency 3651.IBPluginDependency @@ -53183,6 +53303,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 8448.IBPluginDependency 8448.IBViewBoundsToFrameTransform 845.IBPluginDependency + 845.IBViewBoundsToFrameTransform 8451.IBEditorWindowLastContentRect 8451.IBPluginDependency 8451.IBViewBoundsToFrameTransform @@ -53490,7 +53611,11 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 9114.IBPluginDependency 9115.IBPluginDependency 912.IBPluginDependency + 9129.IBPluginDependency 913.IBPluginDependency + 9131.IBPluginDependency + 9133.IBPluginDependency + 9135.IBPluginDependency 914.IBPluginDependency 92.IBPluginDependency 924.IBPluginDependency @@ -54158,6 +54283,9 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABBkAAAxCKAAA + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -56122,9 +56250,9 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 com.apple.InterfaceBuilder.CocoaPlugin - {{343, 181}, {204, 521}} + {{861, 261}, {204, 601}} com.apple.InterfaceBuilder.CocoaPlugin - {{343, 181}, {204, 521}} + {{861, 261}, {204, 601}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -56264,7 +56392,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{884, 253}, {239, 463}} + {{920, 173}, {239, 543}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -56464,6 +56592,9 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABBoAAAwgwAAA + {{1184, 327}, {400, 320}} com.apple.InterfaceBuilder.CocoaPlugin @@ -56746,7 +56877,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 {{307, 552}, {254, 262}} com.apple.InterfaceBuilder.CocoaPlugin {{307, 552}, {254, 262}} - + com.apple.InterfaceBuilder.CocoaPlugin ToolTip @@ -57007,6 +57138,10 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -57025,7 +57160,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 - 9119 + 9136 @@ -57776,6 +57911,7 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 YES YES + autoholdSet: changeAudioEngine: changeCoreEmuFlags: changeCoreSpeed: @@ -57863,12 +57999,14 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 id id id + id YES YES + autoholdSet: changeAudioEngine: changeCoreEmuFlags: changeCoreSpeed: @@ -57914,6 +58052,10 @@ y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp2 YES + + autoholdSet: + id + changeAudioEngine: id diff --git a/desmume/src/cocoa/userinterface/DisplayWindowController.h b/desmume/src/cocoa/userinterface/DisplayWindowController.h index 38b0f5921..dc5d23b75 100644 --- a/desmume/src/cocoa/userinterface/DisplayWindowController.h +++ b/desmume/src/cocoa/userinterface/DisplayWindowController.h @@ -16,7 +16,7 @@ */ #import -#include +#import #include #import "InputManager.h" @@ -24,6 +24,7 @@ @class CocoaDSController; @class EmuControllerDelegate; +class OGLVideoOutput; // Subclass NSWindow for full screen windows so that we can override some methods. @@ -34,59 +35,17 @@ @interface DisplayView : NSView { InputManager *inputManager; - - // Display thread - BOOL isHudEnabled; - BOOL isHudEditingModeEnabled; + OGLVideoOutput *oglv; + CGFloat _displayRotation; // OpenGL context NSOpenGLContext *context; CGLContextObj cglDisplayContext; - - NSSize _currentNormalSize; - NSInteger _currentDisplayMode; - NSInteger _currentDisplayOrientation; - GLfloat _currentGapScalar; - GLfloat _currentRotation; - GLenum glTexPixelFormat; - GLvoid *glTexBack; - NSSize glTexBackSize; - - GLuint displayTexID; - GLuint vboVertexID; - GLuint vboTexCoordID; - GLuint vboElementID; - GLuint vaoMainStatesID; - GLuint vertexShaderID; - GLuint fragmentShaderID; - GLuint shaderProgram; - - GLint uniformAngleDegrees; - GLint uniformScalar; - GLint uniformViewSize; - - GLint vtxBuffer[4 * 8]; - GLfloat texCoordBuffer[2 * 8]; - GLubyte vtxIndexBuffer[12]; - - BOOL isShaderSupported; - size_t vtxBufferOffset; } @property (retain) InputManager *inputManager; -- (void) startupOpenGL; -- (void) shutdownOpenGL; -- (void) setupShaderIO; -- (BOOL) setupShadersWithVertexProgram:(const char *)vertShaderProgram fragmentProgram:(const char *)fragShaderProgram; - (void) drawVideoFrame; -- (void) uploadVertices; -- (void) uploadTexCoords; -- (void) uploadDisplayTextures:(const GLvoid *)textureData displayMode:(const NSInteger)displayModeID width:(const GLsizei)texWidth height:(const GLsizei)texHeight; -- (void) renderDisplayUsingDisplayMode:(const NSInteger)displayModeID; -- (void) updateDisplayVerticesUsingDisplayMode:(const NSInteger)displayModeID orientation:(const NSInteger)displayOrientationID gap:(const GLfloat)gapScalar; -- (void) updateTexCoordS:(GLfloat)s T:(GLfloat)t; - - (NSPoint) dsPointFromEvent:(NSEvent *)theEvent; - (NSPoint) convertPointToDS:(NSPoint)clickLoc; - (BOOL) handleKeyPress:(NSEvent *)theEvent keyPressed:(BOOL)keyPressed; diff --git a/desmume/src/cocoa/userinterface/DisplayWindowController.mm b/desmume/src/cocoa/userinterface/DisplayWindowController.mm index 42f2d4215..2a6eacf0b 100644 --- a/desmume/src/cocoa/userinterface/DisplayWindowController.mm +++ b/desmume/src/cocoa/userinterface/DisplayWindowController.mm @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 DeSmuME team + Copyright (C) 2013-2014 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,10 +25,8 @@ #import "cocoa_videofilter.h" #import "cocoa_util.h" +#include "OGLDisplayOutput.h" #include -#include -#include -#include #if defined(__ppc__) || defined(__ppc64__) #include @@ -36,52 +34,6 @@ #include #endif -// VERTEX SHADER FOR DISPLAY OUTPUT -static const char *vertexProgram_100 = {"\ - attribute vec2 inPosition; \n\ - attribute vec2 inTexCoord0; \n\ - \n\ - uniform vec2 viewSize; \n\ - uniform float scalar; \n\ - uniform float angleDegrees; \n\ - \n\ - varying vec2 vtxTexCoord; \n\ - \n\ - void main() \n\ - { \n\ - float angleRadians = radians(angleDegrees); \n\ - \n\ - mat2 projection = mat2( vec2(2.0/viewSize.x, 0.0), \n\ - vec2( 0.0, 2.0/viewSize.y)); \n\ - \n\ - mat2 rotation = mat2( vec2(cos(angleRadians), -sin(angleRadians)), \n\ - vec2(sin(angleRadians), cos(angleRadians))); \n\ - \n\ - mat2 scale = mat2( vec2(scalar, 0.0), \n\ - vec2( 0.0, scalar)); \n\ - \n\ - vtxTexCoord = inTexCoord0; \n\ - gl_Position = vec4(projection * rotation * scale * inPosition, 1.0, 1.0); \n\ - } \n\ -"}; - -// FRAGMENT SHADER FOR DISPLAY OUTPUT -static const char *fragmentProgram_100 = {"\ - varying vec2 vtxTexCoord; \n\ - uniform sampler2D tex; \n\ - \n\ - void main() \n\ - { \n\ - gl_FragColor = texture2D(tex, vtxTexCoord); \n\ - } \n\ -"}; - -enum OGLVertexAttributeID -{ - OGLVertexAttributeID_Position = 0, - OGLVertexAttributeID_TexCoord0 = 8 -}; - @implementation DisplayWindowController @@ -278,7 +230,7 @@ static std::tr1::unordered_map _screenMap _useBilinearOutput = theState; OSSpinLockUnlock(&spinlockUseBilinearOutput); - [CocoaDSUtil messageSendOneWayWithBool:[[self cdsVideoOutput] receivePort] msgID:MESSAGE_CHANGE_BILINEAR_OUTPUT boolValue:theState]; + [[self view] doBilinearOutputChanged:theState]; } - (BOOL) useBilinearOutput @@ -296,7 +248,7 @@ static std::tr1::unordered_map _screenMap _useVerticalSync = theState; OSSpinLockUnlock(&spinlockUseVerticalSync); - [CocoaDSUtil messageSendOneWayWithBool:[[self cdsVideoOutput] receivePort] msgID:MESSAGE_CHANGE_VERTICAL_SYNC boolValue:theState]; + [[self view] doVerticalSyncChanged:theState]; } - (BOOL) useVerticalSync @@ -1060,13 +1012,6 @@ static std::tr1::unordered_map _screenMap [(NSMenuItem *)theItem setState:([self videoFilterType] == [theItem tag]) ? NSOnState : NSOffState]; } } - else if (theAction == @selector(hudDisable:)) - { - if ([(id)theItem isMemberOfClass:[NSMenuItem class]]) - { - [(NSMenuItem *)theItem setTitle:([[self view] isHudEnabled]) ? NSSTRING_TITLE_DISABLE_HUD : NSSTRING_TITLE_ENABLE_HUD]; - } - } else if (theAction == @selector(toggleStatusBar:)) { if ([(id)theItem isMemberOfClass:[NSMenuItem class]]) @@ -1136,8 +1081,8 @@ static std::tr1::unordered_map _screenMap // Set the video filter source size now since the proper size is needed on initialization. // If we don't do this, new windows could draw incorrectly. - const NSSize vfSrcSize = NSMakeSize(GPU_DISPLAY_WIDTH, ([self displayMode] == DS_DISPLAY_TYPE_DUAL) ? GPU_DISPLAY_HEIGHT * 2 : GPU_DISPLAY_HEIGHT); - [[cdsVideoOutput vf] setSourceSize:vfSrcSize]; + //const NSSize vfSrcSize = NSMakeSize(GPU_DISPLAY_WIDTH, ([self displayMode] == DS_DISPLAY_TYPE_DUAL) ? GPU_DISPLAY_HEIGHT * 2 : GPU_DISPLAY_HEIGHT); + //[[cdsVideoOutput vf] setSourceSize:vfSrcSize]; [CocoaDSUtil messageSendOneWayWithInteger:[cdsVideoOutput receivePort] msgID:MESSAGE_CHANGE_VIDEO_FILTER integerValue:[self videoFilterType]]; // Add the video thread to the output list. @@ -1314,8 +1259,6 @@ static std::tr1::unordered_map _screenMap @implementation DisplayView @synthesize inputManager; -@synthesize isHudEnabled; -@synthesize isHudEditingModeEnabled; - (id)initWithFrame:(NSRect)frameRect { @@ -1326,6 +1269,7 @@ static std::tr1::unordered_map _screenMap } inputManager = nil; + oglv = new OGLVideoOutput(); // Initialize the OpenGL context NSOpenGLPixelFormatAttribute attributes[] = { @@ -1341,31 +1285,9 @@ static std::tr1::unordered_map _screenMap [format release]; cglDisplayContext = (CGLContextObj)[context CGLContextObj]; - _currentDisplayMode = DS_DISPLAY_TYPE_DUAL; - _currentDisplayOrientation = DS_DISPLAY_ORIENTATION_VERTICAL; - _currentGapScalar = 0.0f; - _currentNormalSize = NSMakeSize(GPU_DISPLAY_WIDTH, GPU_DISPLAY_HEIGHT*2.0 + (DS_DISPLAY_GAP*_currentGapScalar)); - glTexPixelFormat = GL_UNSIGNED_SHORT_1_5_5_5_REV; - - const UInt32 w = GetNearestPositivePOT((UInt32)_currentNormalSize.width); - const UInt32 h = GetNearestPositivePOT((UInt32)_currentNormalSize.height); - glTexBack = (GLvoid *)calloc(w * h, sizeof(UInt16)); - glTexBackSize = NSMakeSize(w, h); - vtxBufferOffset = 0; - - [self updateDisplayVerticesUsingDisplayMode:_currentDisplayMode orientation:_currentDisplayOrientation gap:_currentGapScalar]; - [self updateTexCoordS:1.0f T:2.0f]; - - // Set up initial vertex elements - vtxIndexBuffer[0] = 0; vtxIndexBuffer[1] = 1; vtxIndexBuffer[2] = 2; - vtxIndexBuffer[3] = 2; vtxIndexBuffer[4] = 3; vtxIndexBuffer[5] = 0; - - vtxIndexBuffer[6] = 4; vtxIndexBuffer[7] = 5; vtxIndexBuffer[8] = 6; - vtxIndexBuffer[9] = 6; vtxIndexBuffer[10] = 7; vtxIndexBuffer[11] = 4; - CGLContextObj prevContext = CGLGetCurrentContext(); CGLSetCurrentContext(cglDisplayContext); - [self startupOpenGL]; + oglv->InitializeOGL(); CGLSetCurrentContext(prevContext); return self; @@ -1375,350 +1297,26 @@ static std::tr1::unordered_map _screenMap { CGLContextObj prevContext = CGLGetCurrentContext(); CGLSetCurrentContext(cglDisplayContext); - [self shutdownOpenGL]; + oglv->TerminateOGL(); CGLSetCurrentContext(prevContext); - free(glTexBack); - glTexBack = NULL; - [self setInputManager:nil]; [context clearDrawable]; [context release]; + delete oglv; + [super dealloc]; } #pragma mark Class Methods -- (void) startupOpenGL -{ - // Check the OpenGL capabilities for this renderer - const GLubyte *glExtString = glGetString(GL_EXTENSIONS); - - // Set up textures - glGenTextures(1, &displayTexID); - glBindTexture(GL_TEXTURE_2D, displayTexID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(GL_TEXTURE_2D, 0); - - // Set up shaders (but disable on PowerPC, since it doesn't seem to work there) -#if defined(__i386__) || defined(__x86_64__) - isShaderSupported = (gluCheckExtension((const GLubyte *)"GL_ARB_shader_objects", glExtString) && - gluCheckExtension((const GLubyte *)"GL_ARB_vertex_shader", glExtString) && - gluCheckExtension((const GLubyte *)"GL_ARB_fragment_shader", glExtString) && - gluCheckExtension((const GLubyte *)"GL_ARB_vertex_program", glExtString) ); -#else - isShaderSupported = false; -#endif - if (isShaderSupported) - { - BOOL isShaderSetUp = [self setupShadersWithVertexProgram:vertexProgram_100 fragmentProgram:fragmentProgram_100]; - if (isShaderSetUp) - { - glUseProgram(shaderProgram); - - uniformAngleDegrees = glGetUniformLocation(shaderProgram, "angleDegrees"); - uniformScalar = glGetUniformLocation(shaderProgram, "scalar"); - uniformViewSize = glGetUniformLocation(shaderProgram, "viewSize"); - - glUniform1f(uniformAngleDegrees, 0.0f); - glUniform1f(uniformScalar, 1.0f); - glUniform2f(uniformViewSize, GPU_DISPLAY_WIDTH, GPU_DISPLAY_HEIGHT*2.0 + (DS_DISPLAY_GAP*_currentGapScalar)); - } - else - { - isShaderSupported = false; - } - } - - // Set up VBOs - glGenBuffersARB(1, &vboVertexID); - glGenBuffersARB(1, &vboTexCoordID); - glGenBuffersARB(1, &vboElementID); - - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboVertexID); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLint) * (2 * 8), vtxBuffer, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboTexCoordID); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat) * (2 * 8), texCoordBuffer, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - - glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboElementID); - glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLubyte) * 12, vtxIndexBuffer, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); - - // Set up VAO - glGenVertexArraysAPPLE(1, &vaoMainStatesID); - glBindVertexArrayAPPLE(vaoMainStatesID); - - if (isShaderSupported) - { - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboVertexID); - glVertexAttribPointer(OGLVertexAttributeID_Position, 2, GL_INT, GL_FALSE, 0, 0); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboTexCoordID); - glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, 0, 0); - glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboElementID); - - glEnableVertexAttribArray(OGLVertexAttributeID_Position); - glEnableVertexAttribArray(OGLVertexAttributeID_TexCoord0); - } - else - { - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboVertexID); - glVertexPointer(2, GL_INT, 0, 0); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboTexCoordID); - glTexCoordPointer(2, GL_FLOAT, 0, 0); - glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboElementID); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } - - glBindVertexArrayAPPLE(0); - - // Render State Setup (common to both shaders and fixed-function pipeline) - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDisable(GL_DITHER); - glDisable(GL_STENCIL_TEST); - - // Set up fixed-function pipeline render states. - if (!isShaderSupported) - { - glDisable(GL_ALPHA_TEST); - glDisable(GL_LIGHTING); - glDisable(GL_FOG); - glEnable(GL_TEXTURE_2D); - } - - // Set up clear attributes - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); -} - -- (void) shutdownOpenGL -{ - glDeleteTextures(1, &displayTexID); - - glDeleteVertexArraysAPPLE(1, &vaoMainStatesID); - glDeleteBuffersARB(1, &vboVertexID); - glDeleteBuffersARB(1, &vboTexCoordID); - glDeleteBuffersARB(1, &vboElementID); - - if (isShaderSupported) - { - glUseProgram(0); - - glDetachShader(shaderProgram, vertexShaderID); - glDetachShader(shaderProgram, fragmentShaderID); - - glDeleteProgram(shaderProgram); - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - } -} - -- (void) setupShaderIO -{ - glBindAttribLocation(shaderProgram, OGLVertexAttributeID_Position, "inPosition"); - glBindAttribLocation(shaderProgram, OGLVertexAttributeID_TexCoord0, "inTexCoord0"); -} - -- (BOOL) setupShadersWithVertexProgram:(const char *)vertShaderProgram fragmentProgram:(const char *)fragShaderProgram -{ - BOOL result = NO; - GLint shaderStatus = GL_TRUE; - - vertexShaderID = glCreateShader(GL_VERTEX_SHADER); - if (vertexShaderID == 0) - { - NSLog(@"OpenGL Error - Failed to create vertex shader."); - return result; - } - - glShaderSource(vertexShaderID, 1, (const GLchar **)&vertShaderProgram, NULL); - glCompileShader(vertexShaderID); - glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &shaderStatus); - if (shaderStatus == GL_FALSE) - { - glDeleteShader(vertexShaderID); - NSLog(@"OpenGL Error - Failed to compile vertex shader."); - return result; - } - - fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); - if (fragmentShaderID == 0) - { - glDeleteShader(vertexShaderID); - NSLog(@"OpenGL Error - Failed to create fragment shader."); - return result; - } - - glShaderSource(fragmentShaderID, 1, (const GLchar **)&fragShaderProgram, NULL); - glCompileShader(fragmentShaderID); - glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &shaderStatus); - if (shaderStatus == GL_FALSE) - { - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - NSLog(@"OpenGL Error - Failed to compile fragment shader."); - return result; - } - - shaderProgram = glCreateProgram(); - if (shaderProgram == 0) - { - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - NSLog(@"OpenGL Error - Failed to create shader program."); - return result; - } - - glAttachShader(shaderProgram, vertexShaderID); - glAttachShader(shaderProgram, fragmentShaderID); - - [self setupShaderIO]; - - glLinkProgram(shaderProgram); - glGetProgramiv(shaderProgram, GL_LINK_STATUS, &shaderStatus); - if (shaderStatus == GL_FALSE) - { - glDeleteProgram(shaderProgram); - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - NSLog(@"OpenGL Error - Failed to link shader program."); - return result; - } - - glValidateProgram(shaderProgram); - - result = YES; - return result; -} - - (void) drawVideoFrame { + oglv->RenderOGL(); CGLFlushDrawable(cglDisplayContext); } -- (void) uploadVertices -{ - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboVertexID); - glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(GLint) * (2 * 8), vtxBuffer + vtxBufferOffset); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); -} - -- (void) uploadTexCoords -{ - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboTexCoordID); - glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(GLfloat) * (2 * 8), texCoordBuffer); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); -} - -- (void) uploadDisplayTextures:(const GLvoid *)textureData displayMode:(const NSInteger)displayModeID width:(const GLsizei)texWidth height:(const GLsizei)texHeight -{ - if (textureData == NULL) - { - return; - } - - const GLint lineOffset = (displayModeID == DS_DISPLAY_TYPE_TOUCH) ? texHeight : 0; - - glBindTexture(GL_TEXTURE_2D, displayTexID); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, lineOffset, texWidth, texHeight, GL_RGBA, glTexPixelFormat, textureData); - glBindTexture(GL_TEXTURE_2D, 0); -} - -- (void) renderDisplayUsingDisplayMode:(const NSInteger)displayModeID -{ - // Enable vertex attributes - glBindVertexArrayAPPLE(vaoMainStatesID); - - // Perform the render - if (_currentDisplayMode != displayModeID) - { - _currentDisplayMode = displayModeID; - [self updateDisplayVerticesUsingDisplayMode:displayModeID orientation:_currentDisplayOrientation gap:_currentGapScalar]; - [self uploadVertices]; - } - - const GLsizei vtxElementCount = (displayModeID == DS_DISPLAY_TYPE_DUAL) ? 12 : 6; - const GLubyte *elementPointer = !(displayModeID == DS_DISPLAY_TYPE_TOUCH) ? 0 : (GLubyte *)(vtxElementCount * sizeof(GLubyte)); - - glClear(GL_COLOR_BUFFER_BIT); - glBindTexture(GL_TEXTURE_2D, displayTexID); - glDrawElements(GL_TRIANGLES, vtxElementCount, GL_UNSIGNED_BYTE, elementPointer); - glBindTexture(GL_TEXTURE_2D, 0); - - // Disable vertex attributes - glBindVertexArrayAPPLE(0); -} - -- (void) updateDisplayVerticesUsingDisplayMode:(const NSInteger)displayModeID orientation:(const NSInteger)displayOrientationID gap:(const GLfloat)gapScalar -{ - const GLfloat w = GPU_DISPLAY_WIDTH; - const GLfloat h = GPU_DISPLAY_HEIGHT; - const GLfloat gap = DS_DISPLAY_GAP * gapScalar / 2.0; - - if (displayModeID == DS_DISPLAY_TYPE_DUAL) - { - // displayOrder == DS_DISPLAY_ORDER_MAIN_FIRST - if (displayOrientationID == DS_DISPLAY_ORIENTATION_VERTICAL) - { - vtxBuffer[0] = -w/2; vtxBuffer[1] = h+gap; // Top display, top left - vtxBuffer[2] = w/2; vtxBuffer[3] = h+gap; // Top display, top right - vtxBuffer[4] = w/2; vtxBuffer[5] = gap; // Top display, bottom right - vtxBuffer[6] = -w/2; vtxBuffer[7] = gap; // Top display, bottom left - - vtxBuffer[8] = -w/2; vtxBuffer[9] = -gap; // Bottom display, top left - vtxBuffer[10] = w/2; vtxBuffer[11] = -gap; // Bottom display, top right - vtxBuffer[12] = w/2; vtxBuffer[13] = -(h+gap); // Bottom display, bottom right - vtxBuffer[14] = -w/2; vtxBuffer[15] = -(h+gap); // Bottom display, bottom left - } - else // displayOrientationID == DS_DISPLAY_ORIENTATION_HORIZONTAL - { - vtxBuffer[0] = -(w+gap); vtxBuffer[1] = h/2; // Left display, top left - vtxBuffer[2] = -gap; vtxBuffer[3] = h/2; // Left display, top right - vtxBuffer[4] = -gap; vtxBuffer[5] = -h/2; // Left display, bottom right - vtxBuffer[6] = -(w+gap); vtxBuffer[7] = -h/2; // Left display, bottom left - - vtxBuffer[8] = gap; vtxBuffer[9] = h/2; // Right display, top left - vtxBuffer[10] = w+gap; vtxBuffer[11] = h/2; // Right display, top right - vtxBuffer[12] = w+gap; vtxBuffer[13] = -h/2; // Right display, bottom right - vtxBuffer[14] = gap; vtxBuffer[15] = -h/2; // Right display, bottom left - } - - // displayOrder == DS_DISPLAY_ORDER_TOUCH_FIRST - memcpy(vtxBuffer + (2 * 8), vtxBuffer + (1 * 8), sizeof(GLint) * (1 * 8)); - memcpy(vtxBuffer + (3 * 8), vtxBuffer + (0 * 8), sizeof(GLint) * (1 * 8)); - } - else // displayModeID == DS_DISPLAY_TYPE_MAIN || displayModeID == DS_DISPLAY_TYPE_TOUCH - { - vtxBuffer[0] = -w/2; vtxBuffer[1] = h/2; // First display, top left - vtxBuffer[2] = w/2; vtxBuffer[3] = h/2; // First display, top right - vtxBuffer[4] = w/2; vtxBuffer[5] = -h/2; // First display, bottom right - vtxBuffer[6] = -w/2; vtxBuffer[7] = -h/2; // First display, bottom left - - memcpy(vtxBuffer + (1 * 8), vtxBuffer + (0 * 8), sizeof(GLint) * (1 * 8)); // Second display - memcpy(vtxBuffer + (2 * 8), vtxBuffer + (0 * 8), sizeof(GLint) * (2 * 8)); // Second display - } -} - -- (void) updateTexCoordS:(GLfloat)s T:(GLfloat)t -{ - texCoordBuffer[0] = 0.0f; texCoordBuffer[1] = 0.0f; - texCoordBuffer[2] = s; texCoordBuffer[3] = 0.0f; - texCoordBuffer[4] = s; texCoordBuffer[5] = t/2.0f; - texCoordBuffer[6] = 0.0f; texCoordBuffer[7] = t/2.0f; - - texCoordBuffer[8] = 0.0f; texCoordBuffer[9] = t/2.0f; - texCoordBuffer[10] = s; texCoordBuffer[11] = t/2.0f; - texCoordBuffer[12] = s; texCoordBuffer[13] = t; - texCoordBuffer[14] = 0.0f; texCoordBuffer[15] = t; -} - - (NSPoint) dsPointFromEvent:(NSEvent *)theEvent { // Convert the clicked location from window coordinates, to view coordinates, @@ -1742,7 +1340,7 @@ static std::tr1::unordered_map _screenMap const NSSize normalBounds = [windowController normalSize]; const NSSize viewSize = [self bounds].size; - const CGSize transformBounds = GetTransformedBounds(normalBounds.width, normalBounds.height, 1.0, _currentRotation); + const CGSize transformBounds = GetTransformedBounds(normalBounds.width, normalBounds.height, 1.0, _displayRotation); const double s = GetMaxScalarInBounds(transformBounds.width, transformBounds.height, viewSize.width, viewSize.height); CGPoint touchLoc = GetNormalPointFromTransformedPoint(clickLoc.x, clickLoc.y, @@ -2047,13 +1645,23 @@ static std::tr1::unordered_map _screenMap // No init needed, so do nothing. } -- (void)doProcessVideoFrame:(const void *)videoFrameData displayMode:(const NSInteger)displayModeID width:(const NSInteger)frameWidth height:(const NSInteger)frameHeight +- (void)doProcessVideoFrame:(const void *)videoFrameData displayMode:(const NSInteger)frameDisplayMode width:(const NSInteger)frameWidth height:(const NSInteger)frameHeight { + const bool didDisplayModeChange = (oglv->GetDisplayMode() != frameDisplayMode); + if (didDisplayModeChange) + { + oglv->SetDisplayMode(frameDisplayMode); + } + CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - [self uploadDisplayTextures:videoFrameData displayMode:displayModeID width:frameWidth height:frameHeight]; - [self renderDisplayUsingDisplayMode:displayModeID]; + if (didDisplayModeChange) + { + oglv->UploadVerticesOGL(); + } + + oglv->PrerenderOGL(videoFrameData, frameWidth, frameHeight); [self drawVideoFrame]; CGLUnlockContext(cglDisplayContext); @@ -2061,62 +1669,22 @@ static std::tr1::unordered_map _screenMap - (void)doResizeView:(NSRect)rect { - const NSSize viewSize = [self frame].size; - const CGSize checkSize = GetTransformedBounds(_currentNormalSize.width, _currentNormalSize.height, 1.0, _currentRotation); - const double s = GetMaxScalarInBounds(checkSize.width, checkSize.height, viewSize.width, viewSize.height); - CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - - glViewport(0, 0, rect.size.width, rect.size.height); - - if (isShaderSupported) - { - glUniform2f(uniformViewSize, rect.size.width, rect.size.height); - glUniform1f(uniformScalar, s); - } - else - { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(-rect.size.width/2, -rect.size.width/2 + rect.size.width, -rect.size.height/2, -rect.size.height/2 + rect.size.height, -1.0, 1.0); - glRotatef(CLOCKWISE_DEGREES(_currentRotation), 0.0f, 0.0f, 1.0f); - glScalef(s, s, 1.0f); - } - - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; + oglv->SetViewportSizeOGL(rect.size.width, rect.size.height); [self drawVideoFrame]; - CGLUnlockContext(cglDisplayContext); } - (void)doTransformView:(const DisplayOutputTransformData *)transformData { - _currentRotation = (GLfloat)transformData->rotation; - - const NSSize viewSize = [self bounds].size; - const CGSize checkSize = GetTransformedBounds(_currentNormalSize.width, _currentNormalSize.height, 1.0, _currentRotation); - const double s = GetMaxScalarInBounds(checkSize.width, checkSize.height, viewSize.width, viewSize.height); + _displayRotation = (GLfloat)transformData->rotation; + oglv->SetRotation((GLfloat)transformData->rotation); CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - - if (isShaderSupported) - { - glUniform1f(uniformAngleDegrees, _currentRotation); - glUniform1f(uniformScalar, s); - } - else - { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glRotatef(CLOCKWISE_DEGREES(_currentRotation), 0.0f, 0.0f, 1.0f); - glScalef(s, s, 1.0f); - } - - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; + oglv->UpdateDisplayTransformationOGL(); [self drawVideoFrame]; - CGLUnlockContext(cglDisplayContext); } @@ -2124,94 +1692,45 @@ static std::tr1::unordered_map _screenMap { CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; [self drawVideoFrame]; - CGLUnlockContext(cglDisplayContext); } - (void)doDisplayModeChanged:(NSInteger)displayModeID { - DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; - _currentNormalSize = [windowController normalSize]; - - const NSSize viewSize = [self bounds].size; - const CGSize checkSize = GetTransformedBounds(_currentNormalSize.width, _currentNormalSize.height, 1.0, _currentRotation); - const double s = GetMaxScalarInBounds(checkSize.width, checkSize.height, viewSize.width, viewSize.height); - - _currentDisplayMode = displayModeID; - [self updateDisplayVerticesUsingDisplayMode:displayModeID orientation:_currentDisplayOrientation gap:_currentGapScalar]; + oglv->SetDisplayMode(displayModeID); CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - - if (isShaderSupported) - { - glUniform1f(uniformScalar, s); - } - else - { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glRotatef(CLOCKWISE_DEGREES(_currentRotation), 0.0f, 0.0f, 1.0f); - glScalef(s, s, 1.0f); - } - - [self uploadVertices]; - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; + oglv->UpdateDisplayTransformationOGL(); + oglv->UploadVerticesOGL(); [self drawVideoFrame]; - CGLUnlockContext(cglDisplayContext); } - (void)doBilinearOutputChanged:(BOOL)useBilinear { - const GLint textureFilter = (useBilinear) ? GL_LINEAR : GL_NEAREST; + const bool c99_useBilinear = (useBilinear) ? true : false; CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - - glBindTexture(GL_TEXTURE_2D, displayTexID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, textureFilter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, textureFilter); - glBindTexture(GL_TEXTURE_2D, 0); - + oglv->SetDisplayBilinearOGL(c99_useBilinear); + [self drawVideoFrame]; CGLUnlockContext(cglDisplayContext); } - (void)doDisplayOrientationChanged:(NSInteger)displayOrientationID { - DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; - _currentNormalSize = [windowController normalSize]; - - _currentDisplayOrientation = displayOrientationID; - [self updateDisplayVerticesUsingDisplayMode:_currentDisplayMode orientation:displayOrientationID gap:_currentGapScalar]; + oglv->SetDisplayOrientation(displayOrientationID); CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - [self uploadVertices]; + oglv->UploadVerticesOGL(); - if (_currentDisplayMode == DS_DISPLAY_TYPE_DUAL) + if (oglv->GetDisplayMode() == DS_DISPLAY_TYPE_DUAL) { - const NSSize viewSize = [self bounds].size; - const CGSize checkSize = GetTransformedBounds(_currentNormalSize.width, _currentNormalSize.height, 1.0, _currentRotation); - const double s = GetMaxScalarInBounds(checkSize.width, checkSize.height, viewSize.width, viewSize.height); - - if (isShaderSupported) - { - glUniform1f(uniformScalar, s); - } - else - { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glRotatef(CLOCKWISE_DEGREES(_currentRotation), 0.0f, 0.0f, 1.0f); - glScalef(s, s, 1.0f); - } - - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; + oglv->UpdateDisplayTransformationOGL(); [self drawVideoFrame]; } @@ -2220,23 +1739,14 @@ static std::tr1::unordered_map _screenMap - (void)doDisplayOrderChanged:(NSInteger)displayOrderID { - if (displayOrderID == DS_DISPLAY_ORDER_MAIN_FIRST) - { - vtxBufferOffset = 0; - } - else // displayOrder == DS_DISPLAY_ORDER_TOUCH_FIRST - { - vtxBufferOffset = (2 * 8); - } + oglv->SetDisplayOrder(displayOrderID); CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - [self uploadVertices]; - - if (_currentDisplayMode == DS_DISPLAY_TYPE_DUAL) + oglv->UploadVerticesOGL(); + if (oglv->GetDisplayMode() == DS_DISPLAY_TYPE_DUAL) { - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; [self drawVideoFrame]; } @@ -2245,36 +1755,16 @@ static std::tr1::unordered_map _screenMap - (void)doDisplayGapChanged:(float)displayGapScalar { - DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; - _currentNormalSize = [windowController normalSize]; - - _currentGapScalar = (GLfloat)displayGapScalar; - [self updateDisplayVerticesUsingDisplayMode:_currentDisplayMode orientation:_currentDisplayOrientation gap:(GLfloat)displayGapScalar]; + oglv->SetGapScalar((GLfloat)displayGapScalar); CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - [self uploadVertices]; + oglv->UploadVerticesOGL(); - if (_currentDisplayMode == DS_DISPLAY_TYPE_DUAL) + if (oglv->GetDisplayMode() == DS_DISPLAY_TYPE_DUAL) { - const NSSize viewSize = [self bounds].size; - const CGSize checkSize = GetTransformedBounds(_currentNormalSize.width, _currentNormalSize.height, 1.0, _currentRotation); - const double s = GetMaxScalarInBounds(checkSize.width, checkSize.height, viewSize.width, viewSize.height); - - if (isShaderSupported) - { - glUniform1f(uniformScalar, s); - } - else - { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glRotatef(CLOCKWISE_DEGREES(_currentRotation), 0.0f, 0.0f, 1.0f); - glScalef(s, s, 1.0f); - } - - [self renderDisplayUsingDisplayMode:_currentDisplayMode]; + oglv->UpdateDisplayTransformationOGL(); [self drawVideoFrame]; } @@ -2283,57 +1773,19 @@ static std::tr1::unordered_map _screenMap - (void)doVerticalSyncChanged:(BOOL)useVerticalSync { - const GLint swapInt = useVerticalSync ? 1 : 0; - CGLSetParameter(cglDisplayContext, kCGLCPSwapInterval, &swapInt); -} - -- (void)doVideoFilterChanged:(NSInteger)videoFilterTypeID frameSize:(NSSize)videoFilterDestSize -{ - size_t colorDepth = sizeof(uint32_t); - glTexPixelFormat = GL_UNSIGNED_INT_8_8_8_8_REV; - - if (videoFilterTypeID == VideoFilterTypeID_None) - { - colorDepth = sizeof(uint16_t); - glTexPixelFormat = GL_UNSIGNED_SHORT_1_5_5_5_REV; - } - - if (_currentDisplayMode != DS_DISPLAY_TYPE_DUAL) - { - videoFilterDestSize.height = (uint32_t)videoFilterDestSize.height * 2; - } - - // Convert textures to Power-of-Two to support older GPUs - // Example: Radeon X1600M on the 2006 MacBook Pro - const uint32_t potW = GetNearestPositivePOT((uint32_t)videoFilterDestSize.width); - const uint32_t potH = GetNearestPositivePOT((uint32_t)videoFilterDestSize.height); - - if (glTexBackSize.width != potW || glTexBackSize.height != potH) - { - glTexBackSize.width = potW; - glTexBackSize.height = potH; - - free(glTexBack); - glTexBack = (GLvoid *)calloc((size_t)potW * (size_t)potH, colorDepth); - if (glTexBack == NULL) - { - return; - } - } - - const GLfloat s = (GLfloat)videoFilterDestSize.width / (GLfloat)potW; - const GLfloat t = (GLfloat)videoFilterDestSize.height / (GLfloat)potH; - [self updateTexCoordS:s T:t]; + const GLint swapInt = (useVerticalSync) ? 1 : 0; CGLLockContext(cglDisplayContext); CGLSetCurrentContext(cglDisplayContext); - - glBindTexture(GL_TEXTURE_2D, displayTexID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)potW, (GLsizei)potH, 0, GL_BGRA, glTexPixelFormat, glTexBack); - glBindTexture(GL_TEXTURE_2D, 0); - - [self uploadTexCoords]; - + CGLSetParameter(cglDisplayContext, kCGLCPSwapInterval, &swapInt); + CGLUnlockContext(cglDisplayContext); +} + +- (void)doVideoFilterChanged:(NSInteger)videoFilterTypeID +{ + CGLLockContext(cglDisplayContext); + CGLSetCurrentContext(cglDisplayContext); + oglv->RespondToVideoFilterChangeOGL((const VideoFilterTypeID)videoFilterTypeID); CGLUnlockContext(cglDisplayContext); } diff --git a/desmume/src/filter/filter.h b/desmume/src/filter/filter.h index 1d33b8d77..6e0cb14d0 100644 --- a/desmume/src/filter/filter.h +++ b/desmume/src/filter/filter.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2009-2012 DeSmuME team +Copyright (C) 2009-2014 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,3 +41,7 @@ void RenderEPX_1Point5x( SSurface Src, SSurface Dst); void RenderEPXPlus_1Point5x( SSurface Src, SSurface Dst); void RenderNearest_1Point5x( SSurface Src, SSurface Dst); void RenderNearestPlus_1Point5x( SSurface Src, SSurface Dst); +void Render2xBRZ(SSurface Src, SSurface Dst); +void Render3xBRZ(SSurface Src, SSurface Dst); +void Render4xBRZ(SSurface Src, SSurface Dst); +void Render5xBRZ(SSurface Src, SSurface Dst); diff --git a/desmume/src/filter/videofilter.cpp b/desmume/src/filter/videofilter.cpp index 0d283eb21..d1130a845 100644 --- a/desmume/src/filter/videofilter.cpp +++ b/desmume/src/filter/videofilter.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 2011-2012 Roger Manuel - Copyright (C) 2013 DeSmuME team + Copyright (C) 2013-2014 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/desmume/src/filter/videofilter.h b/desmume/src/filter/videofilter.h index df7993e26..b92951d12 100644 --- a/desmume/src/filter/videofilter.h +++ b/desmume/src/filter/videofilter.h @@ -1,6 +1,6 @@ /* Copyright (C) 2011-2012 Roger Manuel - Copyright (C) 2013 DeSmuME team + Copyright (C) 2013-2014 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -64,6 +64,10 @@ enum VideoFilterTypeID VideoFilterTypeID_EPX1_5X, VideoFilterTypeID_EPXPlus1_5X, VideoFilterTypeID_HQ4XS, + VideoFilterTypeID_2xBRZ, + VideoFilterTypeID_3xBRZ, + VideoFilterTypeID_4xBRZ, + VideoFilterTypeID_5xBRZ, VideoFilterTypeIDCount // Make sure this one is always last }; @@ -97,7 +101,11 @@ const VideoFilterAttributes VideoFilterAttributesList[] = { {VideoFilterTypeID_EPXPlus, "EPX+", &RenderEPXPlus, 2, 1}, {VideoFilterTypeID_EPX1_5X, "EPX 1.5x", &RenderEPX_1Point5x, 3, 2}, {VideoFilterTypeID_EPXPlus1_5X, "EPX+ 1.5x", &RenderEPXPlus_1Point5x, 3, 2}, - {VideoFilterTypeID_HQ4XS, "HQ4xS", &RenderHQ4XS, 4, 1} }; + {VideoFilterTypeID_HQ4XS, "HQ4xS", &RenderHQ4XS, 4, 1}, + {VideoFilterTypeID_2xBRZ, "2xBRZ", &Render2xBRZ, 2, 1}, + {VideoFilterTypeID_3xBRZ, "3xBRZ", &Render3xBRZ, 3, 1}, + {VideoFilterTypeID_4xBRZ, "4xBRZ", &Render4xBRZ, 4, 1}, + {VideoFilterTypeID_5xBRZ, "5xBRZ", &Render5xBRZ, 5, 1} }; // VIDEO FILTER PARAMETER DATA TYPES enum VideoFilterParamType diff --git a/desmume/src/filter/xbrz.cpp b/desmume/src/filter/xbrz.cpp new file mode 100644 index 000000000..997f503aa --- /dev/null +++ b/desmume/src/filter/xbrz.cpp @@ -0,0 +1,1267 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +// 2014-02-06 (rogerman): Modified for use in DeSmuME by removing C++11 code. +// Also add render functions compatible with filter.h. + +#include "xbrz.h" +#include "filter.h" +#include +#include +#include + +namespace +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getRed (uint32_t val) { return getByte<2>(val); } +inline unsigned char getGreen(uint32_t val) { return getByte<1>(val); } +inline unsigned char getBlue (uint32_t val) { return getByte<0>(val); } + +template inline +T abs(T value) +{ + //static_assert(std::is_signed::value, ""); + return value < 0 ? -value : value; +} + +const uint32_t redMask = 0xff0000; +const uint32_t greenMask = 0x00ff00; +const uint32_t blueMask = 0x0000ff; + +template inline +void alphaBlend(uint32_t& dst, uint32_t col) //blend color over destination with opacity N / M +{ + //static_assert(N < 256, "possible overflow of (col & redMask) * N"); + //static_assert(M < 256, "possible overflow of (col & redMask ) * N + (dst & redMask ) * (M - N)"); + //static_assert(0 < N && N < M, ""); + dst = (redMask & ((col & redMask ) * N + (dst & redMask ) * (M - N)) / M) | //this works because 8 upper bits are free + (greenMask & ((col & greenMask) * N + (dst & greenMask) * (M - N)) / M) | + (blueMask & ((col & blueMask ) * N + (dst & blueMask ) * (M - N)) / M); +} + + +//inline +//double fastSqrt(double n) +//{ +// __asm //speeds up xBRZ by about 9% compared to std::sqrt +// { +// fld n +// fsqrt +// } +//} +// + + +inline +uint32_t alphaBlend2(uint32_t pix1, uint32_t pix2, double alpha) +{ + return (redMask & static_cast((pix1 & redMask ) * alpha + (pix2 & redMask ) * (1 - alpha))) | + (greenMask & static_cast((pix1 & greenMask) * alpha + (pix2 & greenMask) * (1 - alpha))) | + (blueMask & static_cast((pix1 & blueMask ) * alpha + (pix2 & blueMask ) * (1 - alpha))); +} + + +uint32_t* byteAdvance( uint32_t* ptr, int bytes) { return reinterpret_cast< uint32_t*>(reinterpret_cast< char*>(ptr) + bytes); } +const uint32_t* byteAdvance(const uint32_t* ptr, int bytes) { return reinterpret_cast(reinterpret_cast(ptr) + bytes); } + + +//fill block with the given color +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int n) { fillBlock(trg, pitch, col, n, n); } + + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#elif defined __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE inline +#endif + + +enum RotationDegree //clock-wise +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270 +}; + +//calculate input matrix coordinates after rotation at compile time +template +struct MatrixRotation; + +template +struct MatrixRotation +{ + static const size_t I_old = I; + static const size_t J_old = J; +}; + +template //(i, j) = (row, col) indices, N = size of (square) matrix +struct MatrixRotation +{ + static const size_t I_old = N - 1 - MatrixRotation(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation! + static const size_t J_old = MatrixRotation(rotDeg - 1), I, J, N>::I_old; // +}; + + +template +class OutputMatrix +{ +public: + OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width + out_(out), + outWidth_(outWidth) {} + + template + uint32_t& ref() const + { + static const size_t I_old = MatrixRotation::I_old; + static const size_t J_old = MatrixRotation::J_old; + return *(out_ + J_old + I_old * outWidth_); + } + +private: + uint32_t* out_; + const int outWidth_; +}; + + +template inline +T square(T value) { return value * value; } + + +/* +inline +void rgbtoLuv(uint32_t c, double& L, double& u, double& v) +{ + //http://www.easyrgb.com/index.php?X=MATH&H=02#text2 + double r = getRed (c) / 255.0; + double g = getGreen(c) / 255.0; + double b = getBlue (c) / 255.0; + + if ( r > 0.04045 ) + r = std::pow(( ( r + 0.055 ) / 1.055 ) , 2.4); + else + r /= 12.92; + if ( g > 0.04045 ) + g = std::pow(( ( g + 0.055 ) / 1.055 ) , 2.4); + else + g /= 12.92; + if ( b > 0.04045 ) + b = std::pow(( ( b + 0.055 ) / 1.055 ) , 2.4); + else + b /= 12.92; + + r *= 100; + g *= 100; + b *= 100; + + double x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; + double y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; + double z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; + //--------------------- + double var_U = 4 * x / ( x + 15 * y + 3 * z ); + double var_V = 9 * y / ( x + 15 * y + 3 * z ); + double var_Y = y / 100; + + if ( var_Y > 0.008856 ) var_Y = std::pow(var_Y , 1.0/3 ); + else var_Y = 7.787 * var_Y + 16.0 / 116; + + const double ref_X = 95.047; //Observer= 2°, Illuminant= D65 + const double ref_Y = 100.000; + const double ref_Z = 108.883; + + const double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ); + const double ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ); + + L = ( 116 * var_Y ) - 16; + u = 13 * L * ( var_U - ref_U ); + v = 13 * L * ( var_V - ref_V ); +} +*/ + +inline +void rgbtoLab(uint32_t c, unsigned char& L, signed char& A, signed char& B) +{ + //code: http://www.easyrgb.com/index.php?X=MATH + //test: http://www.workwithcolor.com/color-converter-01.htm + //------RGB to XYZ------ + double r = getRed (c) / 255.0; + double g = getGreen(c) / 255.0; + double b = getBlue (c) / 255.0; + + r = r > 0.04045 ? std::pow(( r + 0.055 ) / 1.055, 2.4) : r / 12.92; + r = g > 0.04045 ? std::pow(( g + 0.055 ) / 1.055, 2.4) : g / 12.92; + r = b > 0.04045 ? std::pow(( b + 0.055 ) / 1.055, 2.4) : b / 12.92; + + r *= 100; + g *= 100; + b *= 100; + + double x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; + double y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; + double z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; + //------XYZ to Lab------ + const double refX = 95.047; // + const double refY = 100.000; //Observer= 2°, Illuminant= D65 + const double refZ = 108.883; // + double var_X = x / refX; + double var_Y = y / refY; + double var_Z = z / refZ; + + var_X = var_X > 0.008856 ? std::pow(var_X, 1.0 / 3) : 7.787 * var_X + 4.0 / 29; + var_Y = var_Y > 0.008856 ? std::pow(var_Y, 1.0 / 3) : 7.787 * var_Y + 4.0 / 29; + var_Z = var_Z > 0.008856 ? std::pow(var_Z, 1.0 / 3) : 7.787 * var_Z + 4.0 / 29; + + L = static_cast(116 * var_Y - 16); + A = static_cast< signed char>(500 * (var_X - var_Y)); + B = static_cast< signed char>(200 * (var_Y - var_Z)); +}; + + +inline +double distLAB(uint32_t pix1, uint32_t pix2) +{ + unsigned char L1 = 0; //[0, 100] + signed char a1 = 0; //[-128, 127] + signed char b1 = 0; //[-128, 127] + rgbtoLab(pix1, L1, a1, b1); + + unsigned char L2 = 0; + signed char a2 = 0; + signed char b2 = 0; + rgbtoLab(pix2, L2, a2, b2); + + //----------------------------- + //http://www.easyrgb.com/index.php?X=DELT + + //Delta E/CIE76 + return std::sqrt(square(1.0 * L1 - L2) + + square(1.0 * a1 - a2) + + square(1.0 * b1 - b2)); +} + + +/* +inline +void rgbtoHsl(uint32_t c, double& h, double& s, double& l) +{ + //http://www.easyrgb.com/index.php?X=MATH&H=18#text18 + const int r = getRed (c); + const int g = getGreen(c); + const int b = getBlue (c); + + const int varMin = numeric::min(r, g, b); + const int varMax = numeric::max(r, g, b); + const int delMax = varMax - varMin; + + l = (varMax + varMin) / 2.0 / 255.0; + + if (delMax == 0) //gray, no chroma... + { + h = 0; + s = 0; + } + else + { + s = l < 0.5 ? + delMax / (1.0 * varMax + varMin) : + delMax / (2.0 * 255 - varMax - varMin); + + double delR = ((varMax - r) / 6.0 + delMax / 2.0) / delMax; + double delG = ((varMax - g) / 6.0 + delMax / 2.0) / delMax; + double delB = ((varMax - b) / 6.0 + delMax / 2.0) / delMax; + + if (r == varMax) + h = delB - delG; + else if (g == varMax) + h = 1 / 3.0 + delR - delB; + else if (b == varMax) + h = 2 / 3.0 + delG - delR; + + if (h < 0) + h += 1; + if (h > 1) + h -= 1; + } +} + +inline +double distHSL(uint32_t pix1, uint32_t pix2, double lightningWeight) +{ + double h1 = 0; + double s1 = 0; + double l1 = 0; + rgbtoHsl(pix1, h1, s1, l1); + double h2 = 0; + double s2 = 0; + double l2 = 0; + rgbtoHsl(pix2, h2, s2, l2); + + //HSL is in cylindric coordinatates where L represents height, S radius, H angle, + //however we interpret the cylinder as a bi-conic solid with top/bottom radius 0, middle radius 1 + assert(0 <= h1 && h1 <= 1); + assert(0 <= h2 && h2 <= 1); + + double r1 = l1 < 0.5 ? + l1 * 2 : + 2 - l1 * 2; + + double x1 = r1 * s1 * std::cos(h1 * 2 * numeric::pi); + double y1 = r1 * s1 * std::sin(h1 * 2 * numeric::pi); + double z1 = l1; + + double r2 = l2 < 0.5 ? + l2 * 2 : + 2 - l2 * 2; + + double x2 = r2 * s2 * std::cos(h2 * 2 * numeric::pi); + double y2 = r2 * s2 * std::sin(h2 * 2 * numeric::pi); + double z2 = l2; + + return 255 * std::sqrt(square(x1 - x2) + square(y1 - y2) + square(lightningWeight * (z1 - z2))); +} +*/ + + +inline +double distRGB(uint32_t pix1, uint32_t pix2) +{ + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //euklidean RGB distance + return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); +} + + +inline +double distNonLinearRGB(uint32_t pix1, uint32_t pix2) +{ + //non-linear rgb: http://www.compuphase.com/cmetric.htm + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + const double r_avg = (static_cast(getRed(pix1)) + getRed(pix2)) / 2; + return std::sqrt((2 + r_avg / 255) * square(r_diff) + 4 * square(g_diff) + (2 + (255 - r_avg) / 255) * square(b_diff)); +} + + +inline +double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) +{ + //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion + //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first! + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); // + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double! + + const double k_b = 0.0722; //ITU-R BT.709 conversion + const double k_r = 0.2126; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + //we skip division by 255 to have similar range like other distance functions + return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)); +} + + +inline +double distYUV(uint32_t pix1, uint32_t pix2, double luminanceWeight) +{ + //perf: it's not worthwhile to buffer the YUV-conversion, the direct code is faster by ~ 6% + //since RGB -> YUV conversion is essentially a matrix multiplication, we can calculate the RGB diff before the conversion (distributive property) + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //http://en.wikipedia.org/wiki/YUV#Conversion_to.2Ffrom_RGB + const double w_b = 0.114; + const double w_r = 0.299; + const double w_g = 1 - w_r - w_b; + + const double u_max = 0.436; + const double v_max = 0.615; + + const double scale_u = u_max / (1 - w_b); + const double scale_v = v_max / (1 - w_r); + + double y = w_r * r_diff + w_g * g_diff + w_b * b_diff;//value range: 255 * [-1, 1] + double u = scale_u * (b_diff - y); //value range: 255 * 2 * u_max * [-1, 1] + double v = scale_v * (r_diff - y); //value range: 255 * 2 * v_max * [-1, 1] + +#ifndef NDEBUG + const double eps = 0.5; +#endif + assert(std::abs(y) <= 255 + eps); + assert(std::abs(u) <= 255 * 2 * u_max + eps); + assert(std::abs(v) <= 255 * 2 * v_max + eps); + + return std::sqrt(square(luminanceWeight * y) + square(u) + square(v)); +} + + +inline +double colorDist(uint32_t pix1, uint32_t pix2, double luminanceWeight) +{ + if (pix1 == pix2) //about 8% perf boost + return 0; + + //return distHSL(pix1, pix2, luminanceWeight); + //return distRGB(pix1, pix2); + //return distLAB(pix1, pix2); + //return distNonLinearRGB(pix1, pix2); + //return distYUV(pix1, pix2, luminanceWeight); + + return distYCbCr(pix1, pix2, luminanceWeight); +} + + +enum BlendType +{ + BLEND_NONE = 0, + BLEND_NORMAL, //a normal indication to blend + BLEND_DOMINANT, //a strong indication to blend + //attention: BlendType must fit into the value range of 2 bit!!! +}; + +struct BlendResult +{ + BlendType + /**/blend_f, blend_g, + /**/blend_j, blend_k; +}; + + +struct Kernel_4x4 //kernel for preprocessing step +{ + uint32_t + /**/a, b, c, d, + /**/e, f, g, h, + /**/i, j, k, l, + /**/m, n, o, p; +}; + +FORCE_INLINE +double ppCornerDist(uint32_t col1, uint32_t col2, const xbrz::ScalerCfg& cfg) +{ + return colorDist(col1, col2, cfg.luminanceWeight_); +} + +/* +input kernel area naming convention: +----------------- +| A | B | C | D | +----|---|---|---| +| E | F | G | H | //evalute the four corners between F, G, J, K +----|---|---|---| //input pixel is at position F +| I | J | K | L | +----|---|---|---| +| M | N | O | P | +----------------- +*/ +FORCE_INLINE //detect blend direction +BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType" +{ + BlendResult result = {}; + + if ((ker.f == ker.g && + ker.j == ker.k) || + (ker.f == ker.j && + ker.g == ker.k)) + return result; + + //auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; + + const int weight = 4; + double jg = ppCornerDist(ker.i, ker.f, cfg) + ppCornerDist(ker.f, ker.c, cfg) + ppCornerDist(ker.n, ker.k, cfg) + ppCornerDist(ker.k, ker.h, cfg) + weight * ppCornerDist(ker.j, ker.g, cfg); + double fk = ppCornerDist(ker.e, ker.j, cfg) + ppCornerDist(ker.j, ker.o, cfg) + ppCornerDist(ker.b, ker.g, cfg) + ppCornerDist(ker.g, ker.l, cfg) + weight * ppCornerDist(ker.f, ker.k, cfg); + + if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 + { + const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; + if (ker.f != ker.g && ker.f != ker.j) + result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.k != ker.j && ker.k != ker.g) + result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + else if (fk < jg) + { + const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg; + if (ker.j != ker.f && ker.j != ker.k) + result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.g != ker.f && ker.g != ker.k) + result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + return result; +} + +struct Kernel_3x3 +{ + uint32_t + /**/a, b, c, + /**/d, e, f, + /**/g, h, i; +}; + +#define DEF_GETTER(x) template uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; } +//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token +DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c) +DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f) +DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a) +DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b) +DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g) +DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d) +DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) +DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +#undef DEF_GETTER + + +//compress four blend types into a single byte +inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } +inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } +inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } + +inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing! +inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } +inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); } +inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); } + +inline bool blendingNeeded(unsigned char b) { return b != 0; } + +template inline +unsigned char rotateBlendInfo(unsigned char b) { return b; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } + + +#ifndef NDEBUG +int debugPixelX = -1; +int debugPixelY = 84; +bool breakIntoDebugger = false; +#endif + +FORCE_INLINE +double sPixEQ(uint32_t col1, uint32_t col2, const xbrz::ScalerCfg& cfg) +{ + return colorDist(col1, col2, cfg.luminanceWeight_) < cfg.equalColorTolerance_; +} + +FORCE_INLINE +double sPixDist(uint32_t col1, uint32_t col2, const xbrz::ScalerCfg& cfg) +{ + return colorDist(col1, col2, cfg.luminanceWeight_); +} + +template +FORCE_INLINE +const bool sPixDoLineBlend(const Kernel_3x3& ker, const char blend, const xbrz::ScalerCfg& cfg) +{ +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !sPixEQ(e, g, cfg)) //but support double-blending for 90° corners + return false; + if (getBottomL(blend) != BLEND_NONE && !sPixEQ(e, c, cfg)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (sPixEQ(g, h, cfg) && sPixEQ(h , i, cfg) && sPixEQ(i, f, cfg) && sPixEQ(f, c, cfg) && !sPixEQ(e, i, cfg)) + return false; + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i + + return true; +} + +/* +input kernel area naming convention: +------------- +| A | B | C | +----|---|---| +| D | E | F | //input pixel is at position E +----|---|---| +| G | H | I | +------------- +*/ +template +FORCE_INLINE //perf: quite worth it! +void scalePixel(const Kernel_3x3& ker, + uint32_t* target, int trgWidth, + unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" + const xbrz::ScalerCfg& cfg) +{ +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) +/* +#ifndef NDEBUG + if (breakIntoDebugger) + __debugbreak(); //__asm int 3; +#endif +*/ + const unsigned char blend = rotateBlendInfo(blendInfo); + + if (getBottomR(blend) >= BLEND_NORMAL) + {/* + auto eq = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_) < cfg.equalColorTolerance_; }; + auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; + + const bool doLineBlend = [&]() -> bool + { + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c) && !eq(e, i)) + return false; + + return true; + }(); +*/ + const uint32_t px = sPixDist(e, f, cfg) <= sPixDist(e, h, cfg) ? f : h; //choose most similar color + + OutputMatrix out(target, trgWidth); + + if (sPixDoLineBlend(ker, blend, cfg)) + { + const double fg = sPixDist(f, g, cfg); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 + const double hc = sPixDist(h, c, cfg); // + + const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g; + const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c; + + if (haveShallowLine) + { + if (haveSteepLine) + Scaler::blendLineSteepAndShallow(px, out); + else + Scaler::blendLineShallow(px, out); + } + else + { + if (haveSteepLine) + Scaler::blendLineSteep(px, out); + else + Scaler::blendLineDiagonal(px,out); + } + } + else + Scaler::blendCorner(px, out); + } + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i +} + +template //scaler policy: see "Scaler2x" reference implementation +void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || srcWidth <= 0) + return; + + const int trgWidth = srcWidth * Scaler::scale; + + //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of + //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing + const int bufferSize = srcWidth; + unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; + std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); + //static_assert(BLEND_NONE == 0, ""); + + //initialize preprocessing buffer for first row: detect upper left and right corner blending + //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! + if (yFirst > 0) + { + const int y = yFirst - 1; + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + for (int x = 0; x < srcWidth; ++x) + { + const int x_m1 = std::max(x - 1, 0); + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker = {}; //perf: initialization is negligable + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //input pixel is at position F + | J | K | + --------- + */ + setTopR(preProcBuffer[x], res.blend_j); + + if (x + 1 < srcWidth) + setTopL(preProcBuffer[x + 1], res.blend_k); + } + } + //------------------------------------------------------------------------------------ + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position + + for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) + { +#ifndef NDEBUG + breakIntoDebugger = debugPixelX == x && debugPixelY == y; +#endif + //all those bounds checks have only insignificant impact on performance! + const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + //evaluate the four corners on bottom-right of current pixel + unsigned char blend_xy = 0; //for current (x, y) position + { + Kernel_4x4 ker = {}; //perf: initialization is negligable + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //current input pixel is at position F + | J | K | + --------- + */ + blend_xy = preProcBuffer[x]; + setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence! + + setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1) + preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row + + blend_xy1 = 0; + setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column + + if (x + 1 < srcWidth) //set 3rd known corner for (x + 1, y) + setBottomL(preProcBuffer[x + 1], res.blend_g); + } + + //fill block of size scale * scale with the given color + fillBlock(out, trgWidth * sizeof(uint32_t), s_0[x], Scaler::scale); //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + + //blend four corners of current pixel + if (blendingNeeded(blend_xy)) //good 20% perf-improvement + { + Kernel_3x3 ker = {}; //perf: initialization is negligable + + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + + ker.d = s_0[x_m1]; + ker.e = s_0[x]; + ker.f = s_0[x_p1]; + + ker.g = s_p1[x_m1]; + ker.h = s_p1[x]; + ker.i = s_p1[x_p1]; + + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + } + } + } +} + + +struct Scaler2x +{ + static const int scale = 2; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<1, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 1>(), col); + alphaBlend<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref<1, 1>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366 + } +}; + + +struct Scaler3x +{ + static const int scale = 3; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + out.template ref<2, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<2, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 2>(), col); + alphaBlend<3, 4>(out.template ref<2, 1>(), col); + alphaBlend<3, 4>(out.template ref<1, 2>(), col); + out.template ref<2, 2>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 8>(out.template ref<1, 2>(), col); + alphaBlend<1, 8>(out.template ref<2, 1>(), col); + alphaBlend<7, 8>(out.template ref<2, 2>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598 + //alphaBlend<14, 1000>(out.template ref<2, 1>(), col); //0.01413008627 -> negligable + //alphaBlend<14, 1000>(out.template ref<1, 2>(), col); //0.01413008627 + } +}; + + +struct Scaler4x +{ + static const int scale = 4; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<3, 4>(out.template ref<3, 1>(), col); + alphaBlend<3, 4>(out.template ref<1, 3>(), col); + alphaBlend<1, 4>(out.template ref<3, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 3>(), col); + alphaBlend<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR + out.template ref<3, 3>() = out.template ref<3, 2>() = out.template ref<2, 3>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563 + alphaBlend< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501 + alphaBlend< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501 + } +}; + + +struct Scaler5x +{ + static const int scale = 5; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<4, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + + out.template ref() = col; + out.template ref() = col; + + out.template ref<4, scale - 1>() = col; + + alphaBlend<2, 3>(out.template ref<3, 3>(), col); + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 8>(out.template ref(), col); + alphaBlend<1, 8>(out.template ref(), col); + alphaBlend<1, 8>(out.template ref(), col); + + alphaBlend<7, 8>(out.template ref<4, 3>(), col); + alphaBlend<7, 8>(out.template ref<3, 4>(), col); + + out.template ref<4, 4>() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088 + alphaBlend<23, 100>(out.template ref<4, 3>(), col); //0.2306749731 + alphaBlend<23, 100>(out.template ref<3, 4>(), col); //0.2306749731 + //alphaBlend<8, 1000>(out.template ref<4, 2>(), col); //0.008384061834 -> negligable + //alphaBlend<8, 1000>(out.template ref<2, 4>(), col); //0.008384061834 + } +}; +} + + +void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + switch (factor) + { + case 2: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + assert(false); +} + + +bool xbrz::equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance) +{ + return colorDist(col1, col2, luminanceWeight) < equalColorTolerance; +} + + +void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast) +{ + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(uint32_t))) + { + assert(false); + return; + } + + switch (st) + { + case NN_SCALE_SLICE_SOURCE: + //nearest-neighbor (going over source image - fast for upscaling, since source is read only once + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrg_last - yTrg_first; + + if (blockHeight > 0) + { + const uint32_t* srcLine = byteAdvance(src, y * srcPitch); + uint32_t* trgLine = byteAdvance(trg, yTrg_first * trgPitch); + int xTrg_first = 0; + + for (int x = 0; x < srcWidth; ++x) + { + int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrg_last - xTrg_first; + if (blockWidth > 0) + { + xTrg_first = xTrg_last; + fillBlock(trgLine, trgPitch, srcLine[x], blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } + break; + + case NN_SCALE_SLICE_TARGET: + //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* trgLine = byteAdvance(trg, y * trgPitch); + const int ySrc = srcHeight * y / trgHeight; + const uint32_t* srcLine = byteAdvance(src, ySrc * srcPitch); + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = srcLine[xSrc]; + } + } + break; + } +} + +void Render2xBRZ(SSurface Src, SSurface Dst) +{ + xbrz::scale(2, (const uint32_t *)Src.Surface, (uint32_t *)Dst.Surface, Src.Width, Src.Height); +} + +void Render3xBRZ(SSurface Src, SSurface Dst) +{ + xbrz::scale(3, (const uint32_t *)Src.Surface, (uint32_t *)Dst.Surface, Src.Width, Src.Height); +} + +void Render4xBRZ(SSurface Src, SSurface Dst) +{ + xbrz::scale(4, (const uint32_t *)Src.Surface, (uint32_t *)Dst.Surface, Src.Width, Src.Height); +} + +void Render5xBRZ(SSurface Src, SSurface Dst) +{ + xbrz::scale(5, (const uint32_t *)Src.Surface, (uint32_t *)Dst.Surface, Src.Width, Src.Height); +} diff --git a/desmume/src/filter/xbrz.h b/desmume/src/filter/xbrz.h new file mode 100644 index 000000000..cc7789f21 --- /dev/null +++ b/desmume/src/filter/xbrz.h @@ -0,0 +1,105 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +// 2014-02-06 (rogerman): Modified for use in DeSmuME by removing C++11 code. +// Also integrate xbrz's config.h file into this one. + +#ifndef XBRZ_HEADER_3847894708239054 +#define XBRZ_HEADER_3847894708239054 + +#include //size_t +#include //uint32_t +#include + +namespace xbrz +{ +/* +------------------------------------------------------------------------- +| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju | +------------------------------------------------------------------------- +using a modified approach of xBR: +http://board.byuu.org/viewtopic.php?f=10&t=2248 +- new rule set preserving small image features +- support multithreading +- support 64 bit architectures +- support processing image slices +*/ + +/* +-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only +-> color format: ARGB (BGRA byte order), alpha channel unused +-> support for source/target pitch in bytes! +-> if your emulator changes only a few image slices during each cycle (e.g. Dosbox) then there's no need to run xBRZ on the complete image: + Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) + Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + if you are using multiple threads for processing each enlarged slice! + +THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only + + +*/ +struct ScalerCfg +{ + ScalerCfg() : + luminanceWeight_(1), + equalColorTolerance_(30), + dominantDirectionThreshold(3.6), + steepDirectionThreshold(2.2), + newTestAttribute_(0) {} + + double luminanceWeight_; + double equalColorTolerance_; + double dominantDirectionThreshold; + double steepDirectionThreshold; + double newTestAttribute_; //unused; test new parameters +}; + +void scale(size_t factor, //valid range: 2 - 5 + const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, + const ScalerCfg& cfg = ScalerCfg(), + int yFirst = 0, int yLast = std::numeric_limits::max()); //slice of source image + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight); + +enum SliceType +{ + NN_SCALE_SLICE_SOURCE, + NN_SCALE_SLICE_TARGET, +}; +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes! + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast); + +//parameter tuning +bool equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance); + + + + + +//########################### implementation ########################### +inline +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + NN_SCALE_SLICE_TARGET, 0, trgHeight); +} +} + +#endif