SDL_render.c 209 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. // The SDL 2D rendering system
  20. #include "SDL_sysrender.h"
  21. #include "SDL_render_debug_font.h"
  22. #include "software/SDL_render_sw_c.h"
  23. #include "../events/SDL_windowevents_c.h"
  24. #include "../video/SDL_pixels_c.h"
  25. #include "../video/SDL_video_c.h"
  26. #ifdef SDL_PLATFORM_ANDROID
  27. #include "../core/android/SDL_android.h"
  28. #include "../video/android/SDL_androidevents.h"
  29. #endif
  30. /* as a courtesy to iOS apps, we don't try to draw when in the background, as
  31. that will crash the app. However, these apps _should_ have used
  32. SDL_AddEventWatch to catch SDL_EVENT_WILL_ENTER_BACKGROUND events and stopped
  33. drawing themselves. Other platforms still draw, as the compositor can use it,
  34. and more importantly: drawing to render targets isn't lost. But I still think
  35. this should probably be removed at some point in the future. --ryan. */
  36. #if defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_OPENHARMONY)
  37. #define DONT_DRAW_WHILE_HIDDEN 1
  38. #else
  39. #define DONT_DRAW_WHILE_HIDDEN 0
  40. #endif
  41. #define SDL_PROP_WINDOW_RENDERER_POINTER "SDL.internal.window.renderer"
  42. #define SDL_PROP_TEXTURE_PARENT_POINTER "SDL.internal.texture.parent"
  43. #define CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer, result) \
  44. CHECK_PARAM(!SDL_ObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER)) { \
  45. SDL_InvalidParamError("renderer"); \
  46. return result; \
  47. }
  48. #define CHECK_RENDERER_MAGIC(renderer, result) \
  49. CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer, result); \
  50. CHECK_PARAM(renderer->destroyed) { \
  51. SDL_SetError("Renderer's window has been destroyed, can't use further"); \
  52. return result; \
  53. }
  54. #define CHECK_TEXTURE_MAGIC(texture, result) \
  55. CHECK_PARAM(!SDL_ObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE)) { \
  56. SDL_InvalidParamError("texture"); \
  57. return result; \
  58. }
  59. // Predefined blend modes
  60. #define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \
  61. srcAlphaFactor, dstAlphaFactor, alphaOperation) \
  62. (SDL_BlendMode)(((Uint32)(colorOperation) << 0) | \
  63. ((Uint32)(srcColorFactor) << 4) | \
  64. ((Uint32)(dstColorFactor) << 8) | \
  65. ((Uint32)(alphaOperation) << 16) | \
  66. ((Uint32)(srcAlphaFactor) << 20) | \
  67. ((Uint32)(dstAlphaFactor) << 24))
  68. #define SDL_BLENDMODE_NONE_FULL \
  69. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \
  70. SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD)
  71. #define SDL_BLENDMODE_BLEND_FULL \
  72. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
  73. SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
  74. #define SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL \
  75. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
  76. SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
  77. #define SDL_BLENDMODE_ADD_FULL \
  78. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \
  79. SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
  80. #define SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL \
  81. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \
  82. SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
  83. #define SDL_BLENDMODE_MOD_FULL \
  84. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \
  85. SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
  86. #define SDL_BLENDMODE_MUL_FULL \
  87. SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
  88. SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
  89. #ifndef SDL_RENDER_DISABLED
  90. static const SDL_RenderDriver *render_drivers[] = {
  91. #ifdef SDL_VIDEO_RENDER_D3D11
  92. &D3D11_RenderDriver,
  93. #endif
  94. #ifdef SDL_VIDEO_RENDER_D3D12
  95. &D3D12_RenderDriver,
  96. #endif
  97. #ifdef SDL_VIDEO_RENDER_D3D
  98. &D3D_RenderDriver,
  99. #endif
  100. #ifdef SDL_VIDEO_RENDER_METAL
  101. &METAL_RenderDriver,
  102. #endif
  103. #ifdef SDL_VIDEO_RENDER_NGAGE
  104. &NGAGE_RenderDriver,
  105. #endif
  106. #ifdef SDL_VIDEO_RENDER_OGL
  107. &GL_RenderDriver,
  108. #endif
  109. #ifdef SDL_VIDEO_RENDER_OGL_ES2
  110. &GLES2_RenderDriver,
  111. #endif
  112. #ifdef SDL_VIDEO_RENDER_OGL_ES
  113. &GLES_RenderDriver,
  114. #endif
  115. #ifdef SDL_VIDEO_RENDER_PS2
  116. &PS2_RenderDriver,
  117. #endif
  118. #ifdef SDL_VIDEO_RENDER_PSP
  119. &PSP_RenderDriver,
  120. #endif
  121. #ifdef SDL_VIDEO_RENDER_VITA_GXM
  122. &VITA_GXM_RenderDriver,
  123. #endif
  124. #ifdef SDL_VIDEO_RENDER_VULKAN
  125. &VULKAN_RenderDriver,
  126. #endif
  127. #ifdef SDL_VIDEO_RENDER_GPU
  128. &GPU_RenderDriver,
  129. #endif
  130. #ifdef SDL_VIDEO_RENDER_SW
  131. &SW_RenderDriver,
  132. #endif
  133. NULL
  134. };
  135. #endif // !SDL_RENDER_DISABLED
  136. static SDL_Renderer *SDL_renderers;
  137. static const int rect_index_order[] = { 0, 1, 2, 0, 2, 3 };
  138. void SDL_QuitRender(void)
  139. {
  140. while (SDL_renderers) {
  141. SDL_DestroyRenderer(SDL_renderers);
  142. }
  143. }
  144. bool SDL_AddSupportedTextureFormat(SDL_Renderer *renderer, SDL_PixelFormat format)
  145. {
  146. SDL_PixelFormat *texture_formats = (SDL_PixelFormat *)SDL_realloc((void *)renderer->texture_formats, (renderer->num_texture_formats + 2) * sizeof(SDL_PixelFormat));
  147. if (!texture_formats) {
  148. return false;
  149. }
  150. texture_formats[renderer->num_texture_formats++] = format;
  151. texture_formats[renderer->num_texture_formats] = SDL_PIXELFORMAT_UNKNOWN;
  152. renderer->texture_formats = texture_formats;
  153. SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, texture_formats);
  154. return true;
  155. }
  156. void SDL_SetupRendererColorspace(SDL_Renderer *renderer, SDL_PropertiesID props)
  157. {
  158. #ifdef SDL_PLATFORM_VISIONOS
  159. // The RealityKit texture always renders in linear colorspace
  160. renderer->output_colorspace = SDL_COLORSPACE_SRGB_LINEAR;
  161. #else
  162. renderer->output_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB);
  163. #endif
  164. renderer->current_colorspace = renderer->output_colorspace;
  165. }
  166. void SDL_ConvertToLinear(SDL_Renderer *renderer, SDL_FColor *color)
  167. {
  168. color->r = SDL_sRGBtoLinear(color->r);
  169. color->g = SDL_sRGBtoLinear(color->g);
  170. color->b = SDL_sRGBtoLinear(color->b);
  171. if (renderer->current_colorspace == SDL_COLORSPACE_HDR10) {
  172. SDL_ConvertColor709to2020(&color->r, &color->g, &color->b);
  173. }
  174. }
  175. static SDL_INLINE void DebugLogRenderCommands(const SDL_RenderCommand *cmd)
  176. {
  177. #if 0
  178. unsigned int i = 1;
  179. SDL_Log("Render commands to flush:");
  180. while (cmd) {
  181. switch (cmd->command) {
  182. case SDL_RENDERCMD_NO_OP:
  183. SDL_Log(" %u. no-op", i++);
  184. break;
  185. case SDL_RENDERCMD_SETVIEWPORT:
  186. SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})", i++,
  187. (unsigned int)cmd->data.viewport.first,
  188. cmd->data.viewport.rect.x, cmd->data.viewport.rect.y,
  189. cmd->data.viewport.rect.w, cmd->data.viewport.rect.h);
  190. break;
  191. case SDL_RENDERCMD_SETCLIPRECT:
  192. SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})", i++,
  193. cmd->data.cliprect.enabled ? "true" : "false",
  194. cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y,
  195. cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h);
  196. break;
  197. case SDL_RENDERCMD_SETDRAWCOLOR:
  198. SDL_Log(" %u. set draw color (first=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, color_scale=%g)", i++,
  199. (unsigned int)cmd->data.color.first,
  200. cmd->data.draw.color.r, cmd->data.draw.color.g,
  201. cmd->data.draw.color.b, cmd->data.draw.color.a, cmd->data.color.color_scale);
  202. break;
  203. case SDL_RENDERCMD_CLEAR:
  204. SDL_Log(" %u. clear (first=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, color_scale=%g)", i++,
  205. (unsigned int)cmd->data.color.first,
  206. cmd->data.draw.color.r, cmd->data.draw.color.g,
  207. cmd->data.draw.color.b, cmd->data.draw.color.a, cmd->data.color.color_scale);
  208. break;
  209. case SDL_RENDERCMD_DRAW_POINTS:
  210. SDL_Log(" %u. draw points (first=%u, count=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, blend=%d, color_scale=%g)", i++,
  211. (unsigned int)cmd->data.draw.first,
  212. (unsigned int)cmd->data.draw.count,
  213. cmd->data.draw.color.r, cmd->data.draw.color.g,
  214. cmd->data.draw.color.b, cmd->data.draw.color.a,
  215. (int)cmd->data.draw.blend, cmd->data.draw.color_scale);
  216. break;
  217. case SDL_RENDERCMD_DRAW_LINES:
  218. SDL_Log(" %u. draw lines (first=%u, count=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, blend=%d, color_scale=%g)", i++,
  219. (unsigned int)cmd->data.draw.first,
  220. (unsigned int)cmd->data.draw.count,
  221. cmd->data.draw.color.r, cmd->data.draw.color.g,
  222. cmd->data.draw.color.b, cmd->data.draw.color.a,
  223. (int)cmd->data.draw.blend, cmd->data.draw.color_scale);
  224. break;
  225. case SDL_RENDERCMD_FILL_RECTS:
  226. SDL_Log(" %u. fill rects (first=%u, count=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, blend=%d, color_scale=%g)", i++,
  227. (unsigned int)cmd->data.draw.first,
  228. (unsigned int)cmd->data.draw.count,
  229. cmd->data.draw.color.r, cmd->data.draw.color.g,
  230. cmd->data.draw.color.b, cmd->data.draw.color.a,
  231. (int)cmd->data.draw.blend, cmd->data.draw.color_scale);
  232. break;
  233. case SDL_RENDERCMD_COPY:
  234. SDL_Log(" %u. copy (first=%u, count=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, blend=%d, color_scale=%g, tex=%p)", i++,
  235. (unsigned int)cmd->data.draw.first,
  236. (unsigned int)cmd->data.draw.count,
  237. cmd->data.draw.color.r, cmd->data.draw.color.g,
  238. cmd->data.draw.color.b, cmd->data.draw.color.a,
  239. (int)cmd->data.draw.blend, cmd->data.draw.color_scale, cmd->data.draw.texture);
  240. break;
  241. case SDL_RENDERCMD_COPY_EX:
  242. SDL_Log(" %u. copyex (first=%u, count=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, blend=%d, color_scale=%g, tex=%p)", i++,
  243. (unsigned int)cmd->data.draw.first,
  244. (unsigned int)cmd->data.draw.count,
  245. cmd->data.draw.color.r, cmd->data.draw.color.g,
  246. cmd->data.draw.color.b, cmd->data.draw.color.a,
  247. (int)cmd->data.draw.blend, cmd->data.draw.color_scale, cmd->data.draw.texture);
  248. break;
  249. case SDL_RENDERCMD_GEOMETRY:
  250. SDL_Log(" %u. geometry (first=%u, count=%u, r=%.2f, g=%.2f, b=%.2f, a=%.2f, blend=%d, color_scale=%g, tex=%p)", i++,
  251. (unsigned int)cmd->data.draw.first,
  252. (unsigned int)cmd->data.draw.count,
  253. cmd->data.draw.color.r, cmd->data.draw.color.g,
  254. cmd->data.draw.color.b, cmd->data.draw.color.a,
  255. (int)cmd->data.draw.blend, cmd->data.draw.color_scale, cmd->data.draw.texture);
  256. break;
  257. }
  258. cmd = cmd->next;
  259. }
  260. #endif
  261. }
  262. static bool FlushRenderCommands(SDL_Renderer *renderer)
  263. {
  264. bool result;
  265. SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
  266. if (!renderer->render_commands) { // nothing to do!
  267. SDL_assert(renderer->vertex_data_used == 0);
  268. return true;
  269. }
  270. DebugLogRenderCommands(renderer->render_commands);
  271. #if DONT_DRAW_WHILE_HIDDEN
  272. // Don't send commands to the GPU while we're hidden
  273. if (renderer->hidden) {
  274. result = true;
  275. } else
  276. #endif
  277. result = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
  278. // Move the whole render command queue to the unused pool so we can reuse them next time.
  279. if (renderer->render_commands_tail) {
  280. renderer->render_commands_tail->next = renderer->render_commands_pool;
  281. renderer->render_commands_pool = renderer->render_commands;
  282. renderer->render_commands_tail = NULL;
  283. renderer->render_commands = NULL;
  284. }
  285. renderer->vertex_data_used = 0;
  286. renderer->render_command_generation++;
  287. renderer->color_queued = false;
  288. renderer->viewport_queued = false;
  289. renderer->cliprect_queued = false;
  290. return result;
  291. }
  292. static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
  293. {
  294. SDL_Renderer *renderer = texture->renderer;
  295. if (texture->last_command_generation == renderer->render_command_generation) {
  296. // the current command queue depends on this texture, flush the queue now before it changes
  297. return FlushRenderCommands(renderer);
  298. }
  299. return true;
  300. }
  301. static bool FlushRenderCommandsIfPaletteNeeded(SDL_Renderer *renderer, SDL_TexturePalette *palette)
  302. {
  303. if (palette->last_command_generation == renderer->render_command_generation) {
  304. // the current command queue depends on this palette, flush the queue now before it changes
  305. return FlushRenderCommands(renderer);
  306. }
  307. return true;
  308. }
  309. static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state)
  310. {
  311. SDL_Renderer *renderer = state->renderer;
  312. if (state->last_command_generation == renderer->render_command_generation) {
  313. // the current command queue depends on this state, flush the queue now before it changes
  314. return FlushRenderCommands(renderer);
  315. }
  316. return true;
  317. }
  318. bool SDL_FlushRenderer(SDL_Renderer *renderer)
  319. {
  320. if (!FlushRenderCommands(renderer)) {
  321. return false;
  322. }
  323. renderer->InvalidateCachedState(renderer);
  324. return true;
  325. }
  326. void *SDL_AllocateRenderVertices(SDL_Renderer *renderer, size_t numbytes, size_t alignment, size_t *offset)
  327. {
  328. const size_t needed = renderer->vertex_data_used + numbytes + alignment;
  329. const size_t current_offset = renderer->vertex_data_used;
  330. const size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0;
  331. const size_t aligned = current_offset + aligner;
  332. if (renderer->vertex_data_allocation < needed) {
  333. const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024;
  334. size_t newsize = current_allocation * 2;
  335. void *ptr;
  336. while (newsize < needed) {
  337. newsize *= 2;
  338. }
  339. ptr = SDL_realloc(renderer->vertex_data, newsize);
  340. if (!ptr) {
  341. return NULL;
  342. }
  343. renderer->vertex_data = ptr;
  344. renderer->vertex_data_allocation = newsize;
  345. }
  346. if (offset) {
  347. *offset = aligned;
  348. }
  349. renderer->vertex_data_used += aligner + numbytes;
  350. return ((Uint8 *)renderer->vertex_data) + aligned;
  351. }
  352. static SDL_RenderCommand *AllocateRenderCommand(SDL_Renderer *renderer)
  353. {
  354. SDL_RenderCommand *result = NULL;
  355. result = renderer->render_commands_pool;
  356. if (result) {
  357. renderer->render_commands_pool = result->next;
  358. result->next = NULL;
  359. } else {
  360. result = (SDL_RenderCommand *)SDL_calloc(1, sizeof(*result));
  361. if (!result) {
  362. return NULL;
  363. }
  364. }
  365. SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
  366. if (renderer->render_commands_tail) {
  367. renderer->render_commands_tail->next = result;
  368. } else {
  369. renderer->render_commands = result;
  370. }
  371. renderer->render_commands_tail = result;
  372. return result;
  373. }
  374. static void UpdatePixelViewport(SDL_Renderer *renderer, SDL_RenderViewState *view)
  375. {
  376. view->pixel_viewport.x = (int)SDL_floorf((view->viewport.x * view->current_scale.x) + view->logical_offset.x);
  377. view->pixel_viewport.y = (int)SDL_floorf((view->viewport.y * view->current_scale.y) + view->logical_offset.y);
  378. if (view->viewport.w >= 0) {
  379. view->pixel_viewport.w = (int)SDL_ceilf(view->viewport.w * view->current_scale.x);
  380. } else {
  381. view->pixel_viewport.w = view->pixel_w;
  382. }
  383. if (view->viewport.h >= 0) {
  384. view->pixel_viewport.h = (int)SDL_ceilf(view->viewport.h * view->current_scale.y);
  385. } else {
  386. view->pixel_viewport.h = view->pixel_h;
  387. }
  388. }
  389. static bool QueueCmdSetViewport(SDL_Renderer *renderer)
  390. {
  391. bool result = true;
  392. SDL_Rect viewport = renderer->view->pixel_viewport;
  393. if (!renderer->viewport_queued ||
  394. SDL_memcmp(&viewport, &renderer->last_queued_viewport, sizeof(viewport)) != 0) {
  395. SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
  396. if (cmd) {
  397. cmd->command = SDL_RENDERCMD_SETVIEWPORT;
  398. cmd->data.viewport.first = 0; // render backend will fill this in.
  399. SDL_copyp(&cmd->data.viewport.rect, &viewport);
  400. result = renderer->QueueSetViewport(renderer, cmd);
  401. if (!result) {
  402. cmd->command = SDL_RENDERCMD_NO_OP;
  403. } else {
  404. SDL_copyp(&renderer->last_queued_viewport, &viewport);
  405. renderer->viewport_queued = true;
  406. }
  407. } else {
  408. result = false;
  409. }
  410. }
  411. return result;
  412. }
  413. static void UpdatePixelClipRect(SDL_Renderer *renderer, SDL_RenderViewState *view)
  414. {
  415. const float scale_x = view->current_scale.x;
  416. const float scale_y = view->current_scale.y;
  417. view->pixel_clip_rect.x = (int)SDL_floorf(view->clip_rect.x * scale_x);
  418. view->pixel_clip_rect.y = (int)SDL_floorf(view->clip_rect.y * scale_y);
  419. view->pixel_clip_rect.w = (int)SDL_ceilf(view->clip_rect.w * scale_x);
  420. view->pixel_clip_rect.h = (int)SDL_ceilf(view->clip_rect.h * scale_y);
  421. }
  422. static bool QueueCmdSetClipRect(SDL_Renderer *renderer)
  423. {
  424. bool result = true;
  425. const SDL_RenderViewState *view = renderer->view;
  426. SDL_Rect clip_rect = view->pixel_clip_rect;
  427. if (!renderer->cliprect_queued ||
  428. view->clipping_enabled != renderer->last_queued_cliprect_enabled ||
  429. SDL_memcmp(&clip_rect, &renderer->last_queued_cliprect, sizeof(clip_rect)) != 0) {
  430. SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
  431. if (cmd) {
  432. cmd->command = SDL_RENDERCMD_SETCLIPRECT;
  433. cmd->data.cliprect.enabled = view->clipping_enabled;
  434. SDL_copyp(&cmd->data.cliprect.rect, &clip_rect);
  435. SDL_copyp(&renderer->last_queued_cliprect, &clip_rect);
  436. renderer->last_queued_cliprect_enabled = view->clipping_enabled;
  437. renderer->cliprect_queued = true;
  438. } else {
  439. result = false;
  440. }
  441. }
  442. return result;
  443. }
  444. static bool QueueCmdSetDrawColor(SDL_Renderer *renderer, SDL_FColor *color)
  445. {
  446. bool result = true;
  447. if (!renderer->color_queued ||
  448. color->r != renderer->last_queued_color.r ||
  449. color->g != renderer->last_queued_color.g ||
  450. color->b != renderer->last_queued_color.b ||
  451. color->a != renderer->last_queued_color.a) {
  452. SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
  453. result = false;
  454. if (cmd) {
  455. cmd->command = SDL_RENDERCMD_SETDRAWCOLOR;
  456. cmd->data.color.first = 0; // render backend will fill this in.
  457. cmd->data.color.color_scale = renderer->color_scale;
  458. cmd->data.color.color = *color;
  459. result = renderer->QueueSetDrawColor(renderer, cmd);
  460. if (!result) {
  461. cmd->command = SDL_RENDERCMD_NO_OP;
  462. } else {
  463. renderer->last_queued_color = *color;
  464. renderer->color_queued = true;
  465. }
  466. }
  467. }
  468. return result;
  469. }
  470. static bool QueueCmdClear(SDL_Renderer *renderer)
  471. {
  472. SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
  473. if (!cmd) {
  474. return false;
  475. }
  476. cmd->command = SDL_RENDERCMD_CLEAR;
  477. cmd->data.color.first = 0;
  478. cmd->data.color.color_scale = renderer->color_scale;
  479. cmd->data.color.color = renderer->color;
  480. return true;
  481. }
  482. static SDL_RenderCommand *PrepQueueCmdDraw(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype, SDL_Texture *texture)
  483. {
  484. SDL_RenderCommand *cmd = NULL;
  485. bool result = true;
  486. SDL_FColor *color;
  487. SDL_BlendMode blendMode;
  488. if (texture) {
  489. color = &texture->color;
  490. blendMode = texture->blendMode;
  491. } else {
  492. color = &renderer->color;
  493. blendMode = renderer->blendMode;
  494. }
  495. if (cmdtype != SDL_RENDERCMD_GEOMETRY) {
  496. result = QueueCmdSetDrawColor(renderer, color);
  497. }
  498. /* Set the viewport and clip rect directly before draws, so the backends
  499. * don't have to worry about that state not being valid at draw time. */
  500. if (result && !renderer->viewport_queued) {
  501. result = QueueCmdSetViewport(renderer);
  502. }
  503. if (result && !renderer->cliprect_queued) {
  504. result = QueueCmdSetClipRect(renderer);
  505. }
  506. if (result) {
  507. cmd = AllocateRenderCommand(renderer);
  508. if (cmd) {
  509. cmd->command = cmdtype;
  510. cmd->data.draw.first = 0; // render backend will fill this in.
  511. cmd->data.draw.count = 0; // render backend will fill this in.
  512. cmd->data.draw.color_scale = renderer->color_scale;
  513. cmd->data.draw.color = *color;
  514. cmd->data.draw.blend = blendMode;
  515. cmd->data.draw.texture = texture;
  516. if (texture) {
  517. cmd->data.draw.texture_scale_mode = texture->scaleMode;
  518. }
  519. cmd->data.draw.texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP;
  520. cmd->data.draw.texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP;
  521. cmd->data.draw.gpu_render_state = renderer->gpu_render_state;
  522. if (renderer->gpu_render_state) {
  523. renderer->gpu_render_state->last_command_generation = renderer->render_command_generation;
  524. }
  525. }
  526. }
  527. return cmd;
  528. }
  529. static bool QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, const int count)
  530. {
  531. SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_DRAW_POINTS, NULL);
  532. bool result = false;
  533. if (cmd) {
  534. result = renderer->QueueDrawPoints(renderer, cmd, points, count);
  535. if (!result) {
  536. cmd->command = SDL_RENDERCMD_NO_OP;
  537. }
  538. }
  539. return result;
  540. }
  541. static bool QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, const int count)
  542. {
  543. SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_DRAW_LINES, NULL);
  544. bool result = false;
  545. if (cmd) {
  546. result = renderer->QueueDrawLines(renderer, cmd, points, count);
  547. if (!result) {
  548. cmd->command = SDL_RENDERCMD_NO_OP;
  549. }
  550. }
  551. return result;
  552. }
  553. static bool QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, const int count)
  554. {
  555. SDL_RenderCommand *cmd;
  556. bool result = false;
  557. const int use_rendergeometry = (!renderer->QueueFillRects);
  558. cmd = PrepQueueCmdDraw(renderer, (use_rendergeometry ? SDL_RENDERCMD_GEOMETRY : SDL_RENDERCMD_FILL_RECTS), NULL);
  559. if (cmd) {
  560. if (use_rendergeometry) {
  561. bool isstack1;
  562. bool isstack2;
  563. float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1);
  564. int *indices = SDL_small_alloc(int, 6 * count, &isstack2);
  565. if (xy && indices) {
  566. int i;
  567. float *ptr_xy = xy;
  568. int *ptr_indices = indices;
  569. const int xy_stride = 2 * sizeof(float);
  570. const int num_vertices = 4 * count;
  571. const int num_indices = 6 * count;
  572. const int size_indices = 4;
  573. int cur_index = 0;
  574. for (i = 0; i < count; ++i) {
  575. float minx, miny, maxx, maxy;
  576. minx = rects[i].x;
  577. miny = rects[i].y;
  578. maxx = rects[i].x + rects[i].w;
  579. maxy = rects[i].y + rects[i].h;
  580. *ptr_xy++ = minx;
  581. *ptr_xy++ = miny;
  582. *ptr_xy++ = maxx;
  583. *ptr_xy++ = miny;
  584. *ptr_xy++ = maxx;
  585. *ptr_xy++ = maxy;
  586. *ptr_xy++ = minx;
  587. *ptr_xy++ = maxy;
  588. *ptr_indices++ = cur_index + rect_index_order[0];
  589. *ptr_indices++ = cur_index + rect_index_order[1];
  590. *ptr_indices++ = cur_index + rect_index_order[2];
  591. *ptr_indices++ = cur_index + rect_index_order[3];
  592. *ptr_indices++ = cur_index + rect_index_order[4];
  593. *ptr_indices++ = cur_index + rect_index_order[5];
  594. cur_index += 4;
  595. }
  596. result = renderer->QueueGeometry(renderer, cmd, NULL,
  597. xy, xy_stride, &renderer->color, 0 /* color_stride */, NULL, 0,
  598. num_vertices, indices, num_indices, size_indices,
  599. 1.0f, 1.0f);
  600. if (!result) {
  601. cmd->command = SDL_RENDERCMD_NO_OP;
  602. }
  603. }
  604. SDL_small_free(xy, isstack1);
  605. SDL_small_free(indices, isstack2);
  606. } else {
  607. result = renderer->QueueFillRects(renderer, cmd, rects, count);
  608. if (!result) {
  609. cmd->command = SDL_RENDERCMD_NO_OP;
  610. }
  611. }
  612. }
  613. return result;
  614. }
  615. static bool UpdateTexturePalette(SDL_Texture *texture)
  616. {
  617. SDL_Renderer *renderer = texture->renderer;
  618. SDL_Palette *public = texture->public_palette;
  619. if (!SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
  620. return true;
  621. }
  622. if (!public) {
  623. return SDL_SetError("Texture doesn't have a palette");
  624. }
  625. if (texture->native) {
  626. // Keep the native texture in sync with palette updates
  627. if (texture->palette_version == public->version) {
  628. return true;
  629. }
  630. if (!FlushRenderCommandsIfTextureNeeded(texture->native)) {
  631. return false;
  632. }
  633. SDL_Surface *surface;
  634. bool result = SDL_LockTextureToSurface(texture->native, NULL, &surface);
  635. if (result) {
  636. result = SDL_BlitSurface(texture->palette_surface, NULL, surface, NULL);
  637. SDL_UnlockTexture(texture->native);
  638. }
  639. if (!result) {
  640. return false;
  641. }
  642. texture->palette_version = public->version;
  643. return true;
  644. }
  645. SDL_TexturePalette *palette = texture->palette;
  646. if (palette->version != public->version) {
  647. // Keep the native palette in sync with palette updates
  648. if (!FlushRenderCommandsIfPaletteNeeded(renderer, palette)) {
  649. return false;
  650. }
  651. if (!renderer->UpdatePalette(renderer, palette, public->ncolors, public->colors)) {
  652. return false;
  653. }
  654. palette->version = public->version;
  655. }
  656. palette->last_command_generation = renderer->render_command_generation;
  657. return true;
  658. }
  659. static bool QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect)
  660. {
  661. SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY, texture);
  662. bool result = false;
  663. if (cmd) {
  664. result = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
  665. if (!result) {
  666. cmd->command = SDL_RENDERCMD_NO_OP;
  667. }
  668. }
  669. return result;
  670. }
  671. static bool QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture *texture,
  672. const SDL_FRect *srcquad, const SDL_FRect *dstrect,
  673. const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y)
  674. {
  675. SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY_EX, texture);
  676. bool result = false;
  677. if (cmd) {
  678. result = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip, scale_x, scale_y);
  679. if (!result) {
  680. cmd->command = SDL_RENDERCMD_NO_OP;
  681. }
  682. }
  683. return result;
  684. }
  685. static bool QueueCmdGeometry(SDL_Renderer *renderer, SDL_Texture *texture,
  686. const float *xy, int xy_stride,
  687. const SDL_FColor *color, int color_stride,
  688. const float *uv, int uv_stride,
  689. int num_vertices,
  690. const void *indices, int num_indices, int size_indices,
  691. float scale_x, float scale_y,
  692. SDL_TextureAddressMode texture_address_mode_u, SDL_TextureAddressMode texture_address_mode_v)
  693. {
  694. SDL_RenderCommand *cmd;
  695. bool result = false;
  696. cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_GEOMETRY, texture);
  697. if (cmd) {
  698. cmd->data.draw.texture_address_mode_u = texture_address_mode_u;
  699. cmd->data.draw.texture_address_mode_v = texture_address_mode_v;
  700. result = renderer->QueueGeometry(renderer, cmd, texture,
  701. xy, xy_stride,
  702. color, color_stride, uv, uv_stride,
  703. num_vertices, indices, num_indices, size_indices,
  704. scale_x, scale_y);
  705. if (!result) {
  706. cmd->command = SDL_RENDERCMD_NO_OP;
  707. }
  708. }
  709. return result;
  710. }
  711. static void UpdateMainViewDimensions(SDL_Renderer *renderer)
  712. {
  713. int window_w = 0, window_h = 0;
  714. if (renderer->window) {
  715. SDL_GetWindowSize(renderer->window, &window_w, &window_h);
  716. }
  717. SDL_GetRenderOutputSize(renderer, &renderer->main_view.pixel_w, &renderer->main_view.pixel_h);
  718. if (window_w > 0 && window_h > 0) {
  719. renderer->dpi_scale.x = (float)renderer->main_view.pixel_w / window_w;
  720. renderer->dpi_scale.y = (float)renderer->main_view.pixel_h / window_h;
  721. } else {
  722. renderer->dpi_scale.x = 1.0f;
  723. renderer->dpi_scale.y = 1.0f;
  724. }
  725. UpdatePixelViewport(renderer, &renderer->main_view);
  726. }
  727. static void UpdateColorScale(SDL_Renderer *renderer)
  728. {
  729. float SDR_white_point;
  730. if (renderer->target) {
  731. SDR_white_point = renderer->target->SDR_white_point;
  732. } else {
  733. SDR_white_point = renderer->SDR_white_point;
  734. }
  735. renderer->color_scale = renderer->desired_color_scale * SDR_white_point;
  736. }
  737. static void UpdateHDRProperties(SDL_Renderer *renderer)
  738. {
  739. SDL_PropertiesID window_props;
  740. SDL_PropertiesID renderer_props;
  741. window_props = SDL_GetWindowProperties(renderer->window);
  742. if (!window_props) {
  743. return;
  744. }
  745. renderer_props = SDL_GetRendererProperties(renderer);
  746. if (!renderer_props) {
  747. return;
  748. }
  749. if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
  750. renderer->output_colorspace == SDL_COLORSPACE_HDR10) {
  751. renderer->SDR_white_point = SDL_GetFloatProperty(window_props, SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT, 1.0f);
  752. renderer->HDR_headroom = SDL_GetFloatProperty(window_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, 1.0f);
  753. } else {
  754. renderer->SDR_white_point = 1.0f;
  755. renderer->HDR_headroom = 1.0f;
  756. }
  757. if (renderer->HDR_headroom > 1.0f) {
  758. SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, true);
  759. } else {
  760. SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, false);
  761. }
  762. SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
  763. SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, renderer->HDR_headroom);
  764. UpdateColorScale(renderer);
  765. }
  766. static void UpdateLogicalPresentation(SDL_Renderer *renderer);
  767. int SDL_GetNumRenderDrivers(void)
  768. {
  769. #ifndef SDL_RENDER_DISABLED
  770. return SDL_arraysize(render_drivers) - 1;
  771. #else
  772. return 0;
  773. #endif
  774. }
  775. const char *SDL_GetRenderDriver(int index)
  776. {
  777. #ifndef SDL_RENDER_DISABLED
  778. CHECK_PARAM(index < 0 || index >= SDL_GetNumRenderDrivers()) {
  779. SDL_InvalidParamError("index");
  780. return NULL;
  781. }
  782. return render_drivers[index]->name;
  783. #else
  784. SDL_SetError("SDL not built with rendering support");
  785. return NULL;
  786. #endif
  787. }
  788. static bool SDL_RendererEventWatch(void *userdata, SDL_Event *event)
  789. {
  790. SDL_Renderer *renderer = (SDL_Renderer *)userdata;
  791. SDL_Window *window = renderer->window;
  792. if (event->window.windowID != SDL_GetWindowID(window)) {
  793. return true;
  794. }
  795. if (renderer->WindowEvent) {
  796. renderer->WindowEvent(renderer, &event->window);
  797. }
  798. if (event->type == SDL_EVENT_WINDOW_RESIZED ||
  799. event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED ||
  800. event->type == SDL_EVENT_WINDOW_METAL_VIEW_RESIZED) {
  801. SDL_RenderViewState *view = renderer->view;
  802. renderer->view = &renderer->main_view; // only update the main_view (the window framebuffer) for window changes.
  803. UpdateLogicalPresentation(renderer);
  804. renderer->view = view; // put us back on whatever the current render target's actual view is.
  805. } else if (event->type == SDL_EVENT_WINDOW_HIDDEN) {
  806. renderer->hidden = true;
  807. } else if (event->type == SDL_EVENT_WINDOW_SHOWN) {
  808. if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) {
  809. renderer->hidden = false;
  810. }
  811. } else if (event->type == SDL_EVENT_WINDOW_MINIMIZED) {
  812. renderer->hidden = true;
  813. } else if (event->type == SDL_EVENT_WINDOW_RESTORED ||
  814. event->type == SDL_EVENT_WINDOW_MAXIMIZED) {
  815. if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
  816. renderer->hidden = false;
  817. }
  818. } else if (event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED ||
  819. event->type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) {
  820. UpdateHDRProperties(renderer);
  821. }
  822. return true;
  823. }
  824. bool SDL_CreateWindowAndRenderer(const char *title, int width, int height, SDL_WindowFlags window_flags, SDL_Window **window, SDL_Renderer **renderer)
  825. {
  826. CHECK_PARAM(!window) {
  827. return SDL_InvalidParamError("window");
  828. }
  829. CHECK_PARAM(!renderer) {
  830. return SDL_InvalidParamError("renderer");
  831. }
  832. // Hide the window so if the renderer recreates it, we don't get a visual flash on screen
  833. bool hidden = (window_flags & SDL_WINDOW_HIDDEN) != 0;
  834. window_flags |= SDL_WINDOW_HIDDEN;
  835. *window = SDL_CreateWindow(title, width, height, window_flags);
  836. if (!*window) {
  837. *renderer = NULL;
  838. return false;
  839. }
  840. *renderer = SDL_CreateRenderer(*window, NULL);
  841. if (!*renderer) {
  842. SDL_DestroyWindow(*window);
  843. *window = NULL;
  844. return false;
  845. }
  846. if (!hidden) {
  847. SDL_ShowWindow(*window);
  848. }
  849. return true;
  850. }
  851. #ifndef SDL_RENDER_DISABLED
  852. static SDL_INLINE void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
  853. {
  854. /* all of these functions are required to be implemented, even as no-ops, so we don't
  855. have to check that they aren't NULL over and over. */
  856. SDL_assert(renderer->QueueSetViewport != NULL);
  857. SDL_assert(renderer->QueueSetDrawColor != NULL);
  858. SDL_assert(renderer->QueueDrawPoints != NULL);
  859. SDL_assert(renderer->QueueDrawLines != NULL || renderer->QueueGeometry != NULL);
  860. SDL_assert(renderer->QueueFillRects != NULL || renderer->QueueGeometry != NULL);
  861. SDL_assert(renderer->QueueCopy != NULL || renderer->QueueGeometry != NULL);
  862. SDL_assert(renderer->RunCommandQueue != NULL);
  863. }
  864. static SDL_RenderLineMethod SDL_GetRenderLineMethod(void)
  865. {
  866. const char *hint = SDL_GetHint(SDL_HINT_RENDER_LINE_METHOD);
  867. int method = 0;
  868. if (hint) {
  869. method = SDL_atoi(hint);
  870. }
  871. switch (method) {
  872. case 1:
  873. return SDL_RENDERLINEMETHOD_POINTS;
  874. case 2:
  875. return SDL_RENDERLINEMETHOD_LINES;
  876. case 3:
  877. return SDL_RENDERLINEMETHOD_GEOMETRY;
  878. default:
  879. return SDL_RENDERLINEMETHOD_POINTS;
  880. }
  881. }
  882. static void SDL_CalculateSimulatedVSyncInterval(SDL_Renderer *renderer, SDL_Window *window)
  883. {
  884. SDL_DisplayID displayID = window ? SDL_GetDisplayForWindow(window) : 0;
  885. const SDL_DisplayMode *mode;
  886. int refresh_num, refresh_den;
  887. if (displayID == 0) {
  888. displayID = SDL_GetPrimaryDisplay();
  889. }
  890. mode = SDL_GetDesktopDisplayMode(displayID);
  891. if (mode && mode->refresh_rate_numerator > 0 && mode->refresh_rate_denominator > 0) {
  892. refresh_num = mode->refresh_rate_numerator;
  893. refresh_den = mode->refresh_rate_denominator;
  894. } else {
  895. // Pick a good default refresh rate
  896. refresh_num = 60;
  897. refresh_den = 1;
  898. }
  899. // Flip numerator and denominator to change from framerate to interval
  900. renderer->simulate_vsync_interval_ns = (SDL_NS_PER_SECOND * refresh_den) / refresh_num;
  901. }
  902. #endif // !SDL_RENDER_DISABLED
  903. SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
  904. {
  905. #ifndef SDL_RENDER_DISABLED
  906. SDL_Window *window = (SDL_Window *)SDL_GetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, NULL);
  907. SDL_Surface *surface = (SDL_Surface *)SDL_GetPointerProperty(props, SDL_PROP_RENDERER_CREATE_SURFACE_POINTER, NULL);
  908. const char *driver_name = SDL_GetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, NULL);
  909. const char *hint;
  910. SDL_PropertiesID new_props;
  911. // The GPU renderer is the only one that can be created without a window or surface
  912. CHECK_PARAM(!window && !surface && (!driver_name || SDL_strcmp(driver_name, SDL_GPU_RENDERER) != 0)) {
  913. SDL_InvalidParamError("window");
  914. return NULL;
  915. }
  916. CHECK_PARAM(window && surface) {
  917. SDL_SetError("A renderer can't target both a window and surface");
  918. return NULL;
  919. }
  920. CHECK_PARAM(window && SDL_WindowHasSurface(window)) {
  921. SDL_SetError("Surface already associated with window");
  922. return NULL;
  923. }
  924. CHECK_PARAM(window && SDL_GetRenderer(window)) {
  925. SDL_SetError("Renderer already associated with window");
  926. return NULL;
  927. }
  928. #ifdef SDL_PLATFORM_ANDROID
  929. if (!Android_WaitActiveAndLockActivity()) {
  930. return NULL;
  931. }
  932. #endif
  933. SDL_Renderer *renderer = (SDL_Renderer *)SDL_calloc(1, sizeof(*renderer));
  934. if (!renderer) {
  935. goto error;
  936. }
  937. SDL_SetObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER, true);
  938. hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC);
  939. if (hint && *hint) {
  940. SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, true));
  941. }
  942. if (surface) {
  943. #ifdef SDL_VIDEO_RENDER_SW
  944. const bool rc = SW_CreateRendererForSurface(renderer, surface, props);
  945. #else
  946. const bool rc = SDL_SetError("SDL not built with software renderer");
  947. #endif
  948. if (!rc) {
  949. goto error;
  950. }
  951. } else {
  952. char *driver_error = NULL;
  953. bool rc = false;
  954. if (!driver_name) {
  955. driver_name = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
  956. }
  957. if (driver_name && *driver_name != 0) {
  958. const char *driver_attempt = driver_name;
  959. while (driver_attempt && *driver_attempt != 0 && !rc) {
  960. const char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
  961. const size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt) : SDL_strlen(driver_attempt);
  962. for (int i = 0; render_drivers[i]; i++) {
  963. const SDL_RenderDriver *driver = render_drivers[i];
  964. if ((driver_attempt_len == SDL_strlen(driver->name)) && (SDL_strncasecmp(driver->name, driver_attempt, driver_attempt_len) == 0)) {
  965. if (driver_error) {
  966. // Free any previous driver error
  967. SDL_free(driver_error);
  968. driver_error = NULL;
  969. }
  970. rc = driver->CreateRenderer(renderer, window, props);
  971. if (rc) {
  972. break;
  973. }
  974. driver_error = SDL_strdup(SDL_GetError());
  975. SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, "Couldn't create renderer %s: %s\n", driver->name, driver_error);
  976. SDL_DestroyRendererWithoutFreeing(renderer);
  977. SDL_zerop(renderer); // make sure we don't leave function pointers from a previous CreateRenderer() in this struct.
  978. }
  979. }
  980. driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
  981. }
  982. } else {
  983. for (int i = 0; render_drivers[i]; i++) {
  984. const SDL_RenderDriver *driver = render_drivers[i];
  985. rc = driver->CreateRenderer(renderer, window, props);
  986. if (rc) {
  987. break;
  988. }
  989. SDL_DestroyRendererWithoutFreeing(renderer);
  990. SDL_zerop(renderer); // make sure we don't leave function pointers from a previous CreateRenderer() in this struct.
  991. }
  992. }
  993. if (rc) {
  994. SDL_DebugLogBackend("render", renderer->name);
  995. SDL_free(driver_error);
  996. } else {
  997. if (driver_name) {
  998. if (driver_error) {
  999. SDL_SetError("%s", driver_error);
  1000. } else {
  1001. SDL_SetError("%s not available", driver_name);
  1002. }
  1003. } else {
  1004. SDL_SetError("Couldn't find matching render driver");
  1005. }
  1006. SDL_free(driver_error);
  1007. goto error;
  1008. }
  1009. }
  1010. VerifyDrawQueueFunctions(renderer);
  1011. renderer->window = window;
  1012. renderer->target_mutex = SDL_CreateMutex();
  1013. if (surface) {
  1014. renderer->main_view.pixel_w = surface->w;
  1015. renderer->main_view.pixel_h = surface->h;
  1016. }
  1017. renderer->main_view.viewport.w = -1.0f;
  1018. renderer->main_view.viewport.h = -1.0f;
  1019. renderer->main_view.scale.x = 1.0f;
  1020. renderer->main_view.scale.y = 1.0f;
  1021. renderer->main_view.logical_scale.x = 1.0f;
  1022. renderer->main_view.logical_scale.y = 1.0f;
  1023. renderer->main_view.current_scale.x = 1.0f;
  1024. renderer->main_view.current_scale.y = 1.0f;
  1025. renderer->view = &renderer->main_view;
  1026. renderer->dpi_scale.x = 1.0f;
  1027. renderer->dpi_scale.y = 1.0f;
  1028. UpdatePixelViewport(renderer, &renderer->main_view);
  1029. UpdatePixelClipRect(renderer, &renderer->main_view);
  1030. UpdateMainViewDimensions(renderer);
  1031. renderer->palettes = SDL_CreateHashTable(0, false, SDL_HashPointer, SDL_KeyMatchPointer, SDL_DestroyHashValue, NULL);
  1032. if (!renderer->palettes) {
  1033. goto error;
  1034. }
  1035. // new textures start at zero, so we start at 1 so first render doesn't flush by accident.
  1036. renderer->render_command_generation = 1;
  1037. if (renderer->software) {
  1038. // Software renderer always uses line method, for speed
  1039. renderer->line_method = SDL_RENDERLINEMETHOD_LINES;
  1040. } else {
  1041. renderer->line_method = SDL_GetRenderLineMethod();
  1042. }
  1043. renderer->scale_mode = SDL_SCALEMODE_LINEAR;
  1044. renderer->SDR_white_point = 1.0f;
  1045. renderer->HDR_headroom = 1.0f;
  1046. renderer->desired_color_scale = 1.0f;
  1047. renderer->color_scale = 1.0f;
  1048. if (window) {
  1049. if (SDL_GetWindowFlags(window) & SDL_WINDOW_TRANSPARENT) {
  1050. renderer->transparent_window = true;
  1051. }
  1052. if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) {
  1053. renderer->hidden = true;
  1054. }
  1055. }
  1056. new_props = SDL_GetRendererProperties(renderer);
  1057. SDL_SetStringProperty(new_props, SDL_PROP_RENDERER_NAME_STRING, renderer->name);
  1058. if (window) {
  1059. SDL_SetPointerProperty(new_props, SDL_PROP_RENDERER_WINDOW_POINTER, window);
  1060. }
  1061. if (surface) {
  1062. SDL_SetPointerProperty(new_props, SDL_PROP_RENDERER_SURFACE_POINTER, surface);
  1063. }
  1064. SDL_SetNumberProperty(new_props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, renderer->output_colorspace);
  1065. SDL_SetBooleanProperty(new_props, SDL_PROP_RENDERER_TEXTURE_WRAPPING_BOOLEAN, !renderer->npot_texture_wrap_unsupported);
  1066. if (window) {
  1067. UpdateHDRProperties(renderer);
  1068. SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, renderer);
  1069. SDL_AddWindowRenderer(window, renderer);
  1070. }
  1071. SDL_SetRenderViewport(renderer, NULL);
  1072. if (window) {
  1073. SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer);
  1074. }
  1075. int vsync = (int)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0);
  1076. SDL_SetRenderVSync(renderer, vsync);
  1077. SDL_CalculateSimulatedVSyncInterval(renderer, window);
  1078. SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
  1079. "Created renderer: %s", renderer->name);
  1080. renderer->next = SDL_renderers;
  1081. SDL_renderers = renderer;
  1082. #ifdef SDL_PLATFORM_ANDROID
  1083. Android_UnlockActivityMutex();
  1084. #endif
  1085. SDL_ClearError();
  1086. return renderer;
  1087. error:
  1088. #ifdef SDL_PLATFORM_ANDROID
  1089. Android_UnlockActivityMutex();
  1090. #endif
  1091. if (renderer) {
  1092. SDL_DestroyRenderer(renderer);
  1093. }
  1094. return NULL;
  1095. #else
  1096. SDL_SetError("SDL not built with rendering support");
  1097. return NULL;
  1098. #endif
  1099. }
  1100. SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name)
  1101. {
  1102. SDL_Renderer *renderer;
  1103. SDL_PropertiesID props = SDL_CreateProperties();
  1104. SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
  1105. SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, name);
  1106. renderer = SDL_CreateRendererWithProperties(props);
  1107. SDL_DestroyProperties(props);
  1108. return renderer;
  1109. }
  1110. SDL_Renderer *SDL_CreateGPURenderer(SDL_GPUDevice *device, SDL_Window *window)
  1111. {
  1112. SDL_Renderer *renderer;
  1113. SDL_PropertiesID props = SDL_CreateProperties();
  1114. SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_GPU_DEVICE_POINTER, device);
  1115. SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
  1116. SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, SDL_GPU_RENDERER);
  1117. renderer = SDL_CreateRendererWithProperties(props);
  1118. SDL_DestroyProperties(props);
  1119. return renderer;
  1120. }
  1121. SDL_GPUDevice *SDL_GetGPURendererDevice(SDL_Renderer *renderer)
  1122. {
  1123. CHECK_RENDERER_MAGIC(renderer, NULL);
  1124. SDL_GPUDevice *device = SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);
  1125. if (!device) {
  1126. SDL_SetError("Renderer isn't a GPU renderer");
  1127. return NULL;
  1128. }
  1129. return device;
  1130. }
  1131. SDL_Renderer *SDL_CreateSoftwareRenderer(SDL_Surface *surface)
  1132. {
  1133. #ifdef SDL_VIDEO_RENDER_SW
  1134. SDL_Renderer *renderer;
  1135. CHECK_PARAM(!surface) {
  1136. SDL_InvalidParamError("surface");
  1137. return NULL;
  1138. }
  1139. SDL_PropertiesID props = SDL_CreateProperties();
  1140. SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_SURFACE_POINTER, surface);
  1141. renderer = SDL_CreateRendererWithProperties(props);
  1142. SDL_DestroyProperties(props);
  1143. return renderer;
  1144. #else
  1145. SDL_SetError("SDL not built with rendering support");
  1146. return NULL;
  1147. #endif // SDL_VIDEO_RENDER_SW
  1148. }
  1149. SDL_Renderer *SDL_GetRenderer(SDL_Window *window)
  1150. {
  1151. return (SDL_Renderer *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, NULL);
  1152. }
  1153. SDL_Window *SDL_GetRenderWindow(SDL_Renderer *renderer)
  1154. {
  1155. CHECK_RENDERER_MAGIC(renderer, NULL);
  1156. return renderer->window;
  1157. }
  1158. const char *SDL_GetRendererName(SDL_Renderer *renderer)
  1159. {
  1160. CHECK_RENDERER_MAGIC(renderer, NULL);
  1161. return SDL_GetPersistentString(renderer->name);
  1162. }
  1163. SDL_PropertiesID SDL_GetRendererProperties(SDL_Renderer *renderer)
  1164. {
  1165. CHECK_RENDERER_MAGIC(renderer, 0);
  1166. if (renderer->props == 0) {
  1167. renderer->props = SDL_CreateProperties();
  1168. }
  1169. return renderer->props;
  1170. }
  1171. bool SDL_GetRenderOutputSize(SDL_Renderer *renderer, int *w, int *h)
  1172. {
  1173. if (w) {
  1174. *w = 0;
  1175. }
  1176. if (h) {
  1177. *h = 0;
  1178. }
  1179. CHECK_RENDERER_MAGIC(renderer, false);
  1180. if (renderer->GetOutputSize) {
  1181. return renderer->GetOutputSize(renderer, w, h);
  1182. } else if (renderer->window) {
  1183. return SDL_GetWindowSizeInPixels(renderer->window, w, h);
  1184. } else {
  1185. // We don't have any output size, this might be an offscreen-only renderer
  1186. return true;
  1187. }
  1188. }
  1189. bool SDL_GetCurrentRenderOutputSize(SDL_Renderer *renderer, int *w, int *h)
  1190. {
  1191. if (w) {
  1192. *w = 0;
  1193. }
  1194. if (h) {
  1195. *h = 0;
  1196. }
  1197. CHECK_RENDERER_MAGIC(renderer, false);
  1198. const SDL_RenderViewState *view = renderer->view;
  1199. if (w) {
  1200. *w = view->pixel_w;
  1201. }
  1202. if (h) {
  1203. *h = view->pixel_h;
  1204. }
  1205. return true;
  1206. }
  1207. static bool IsSupportedBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
  1208. {
  1209. switch (blendMode) {
  1210. // These are required to be supported by all renderers
  1211. case SDL_BLENDMODE_NONE:
  1212. case SDL_BLENDMODE_BLEND:
  1213. case SDL_BLENDMODE_BLEND_PREMULTIPLIED:
  1214. case SDL_BLENDMODE_ADD:
  1215. case SDL_BLENDMODE_ADD_PREMULTIPLIED:
  1216. case SDL_BLENDMODE_MOD:
  1217. case SDL_BLENDMODE_MUL:
  1218. return true;
  1219. default:
  1220. return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode);
  1221. }
  1222. }
  1223. static bool IsSupportedFormat(SDL_Renderer *renderer, SDL_PixelFormat format)
  1224. {
  1225. int i;
  1226. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1227. if (renderer->texture_formats[i] == format) {
  1228. return true;
  1229. }
  1230. }
  1231. return false;
  1232. }
  1233. static SDL_PixelFormat GetClosestSupportedFormat(SDL_Renderer *renderer, SDL_PixelFormat format, bool hasAlpha)
  1234. {
  1235. int i;
  1236. if (format == SDL_PIXELFORMAT_MJPG) {
  1237. // We'll decode to SDL_PIXELFORMAT_NV12 or SDL_PIXELFORMAT_RGBA32
  1238. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1239. if (renderer->texture_formats[i] == SDL_PIXELFORMAT_NV12) {
  1240. return renderer->texture_formats[i];
  1241. }
  1242. }
  1243. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1244. if (renderer->texture_formats[i] == SDL_PIXELFORMAT_RGBA32) {
  1245. return renderer->texture_formats[i];
  1246. }
  1247. }
  1248. } else if (SDL_ISPIXELFORMAT_FOURCC(format)) {
  1249. // Look for an exact match
  1250. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1251. if (renderer->texture_formats[i] == format) {
  1252. return renderer->texture_formats[i];
  1253. }
  1254. }
  1255. } else if (SDL_ISPIXELFORMAT_10BIT(format) || SDL_ISPIXELFORMAT_FLOAT(format)) {
  1256. if (SDL_ISPIXELFORMAT_10BIT(format)) {
  1257. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1258. if (SDL_ISPIXELFORMAT_10BIT(renderer->texture_formats[i])) {
  1259. return renderer->texture_formats[i];
  1260. }
  1261. }
  1262. }
  1263. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1264. if (SDL_ISPIXELFORMAT_FLOAT(renderer->texture_formats[i])) {
  1265. return renderer->texture_formats[i];
  1266. }
  1267. }
  1268. } else if (SDL_ISPIXELFORMAT_INDEXED(format)) {
  1269. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1270. // Converting between <8bpp formats is not supported yet
  1271. if (renderer->texture_formats[i] == SDL_PIXELFORMAT_INDEX8) {
  1272. return renderer->texture_formats[i];
  1273. }
  1274. }
  1275. } else {
  1276. int type = SDL_PIXELTYPE(format);
  1277. int layout = SDL_PIXELLAYOUT(format);
  1278. hasAlpha = hasAlpha || SDL_ISPIXELFORMAT_ALPHA(format);
  1279. // We just want to match the first format that has the same channels
  1280. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1281. if (!SDL_ISPIXELFORMAT_FOURCC(renderer->texture_formats[i]) &&
  1282. SDL_PIXELTYPE(renderer->texture_formats[i]) == type &&
  1283. SDL_PIXELLAYOUT(renderer->texture_formats[i]) == layout &&
  1284. SDL_ISPIXELFORMAT_ALPHA(renderer->texture_formats[i]) == hasAlpha) {
  1285. return renderer->texture_formats[i];
  1286. }
  1287. }
  1288. }
  1289. if (hasAlpha) {
  1290. // The default format may still lack an alpha channel
  1291. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1292. if (!SDL_ISPIXELFORMAT_FOURCC(renderer->texture_formats[i]) &&
  1293. SDL_ISPIXELFORMAT_ALPHA(renderer->texture_formats[i])) {
  1294. return renderer->texture_formats[i];
  1295. }
  1296. }
  1297. }
  1298. return renderer->texture_formats[0];
  1299. }
  1300. SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props)
  1301. {
  1302. SDL_Texture *texture;
  1303. SDL_PixelFormat format = (SDL_PixelFormat)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, SDL_PIXELFORMAT_UNKNOWN);
  1304. SDL_TextureAccess access = (SDL_TextureAccess)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
  1305. int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0);
  1306. int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0);
  1307. SDL_Palette *palette = (SDL_Palette *)SDL_GetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER, NULL);
  1308. SDL_Colorspace default_colorspace;
  1309. bool texture_is_fourcc_and_target;
  1310. CHECK_RENDERER_MAGIC(renderer, NULL);
  1311. if (!format) {
  1312. format = renderer->texture_formats[0];
  1313. }
  1314. CHECK_PARAM(SDL_BYTESPERPIXEL(format) == 0) {
  1315. SDL_SetError("Invalid texture format");
  1316. return NULL;
  1317. }
  1318. CHECK_PARAM(SDL_ISPIXELFORMAT_INDEXED(format) && access == SDL_TEXTUREACCESS_TARGET) {
  1319. SDL_SetError("Palettized textures can't be render targets");
  1320. return NULL;
  1321. }
  1322. CHECK_PARAM(SDL_ISPIXELFORMAT_FOURCC(format) && access == SDL_TEXTUREACCESS_TARGET) {
  1323. SDL_SetError("%s textures can't be render targets", SDL_GetPixelFormatName(format));
  1324. return NULL;
  1325. }
  1326. CHECK_PARAM(w <= 0 || h <= 0) {
  1327. SDL_SetError("Texture dimensions can't be 0");
  1328. return NULL;
  1329. }
  1330. int max_texture_size = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 0);
  1331. CHECK_PARAM(max_texture_size && (w > max_texture_size || h > max_texture_size)) {
  1332. SDL_SetError("Texture dimensions are limited to %dx%d", max_texture_size, max_texture_size);
  1333. return NULL;
  1334. }
  1335. default_colorspace = SDL_GetDefaultColorspaceForFormat(format);
  1336. texture = (SDL_Texture *)SDL_calloc(1, sizeof(*texture));
  1337. if (!texture) {
  1338. return NULL;
  1339. }
  1340. texture->refcount = 1;
  1341. SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, true);
  1342. texture->colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace);
  1343. texture->format = format;
  1344. texture->access = access;
  1345. texture->w = w;
  1346. texture->h = h;
  1347. texture->color.r = 1.0f;
  1348. texture->color.g = 1.0f;
  1349. texture->color.b = 1.0f;
  1350. texture->color.a = 1.0f;
  1351. texture->blendMode = SDL_ISPIXELFORMAT_ALPHA(format) ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE;
  1352. texture->scaleMode = renderer->scale_mode;
  1353. texture->view.pixel_w = w;
  1354. texture->view.pixel_h = h;
  1355. texture->view.viewport.w = -1.0f;
  1356. texture->view.viewport.h = -1.0f;
  1357. texture->view.scale.x = 1.0f;
  1358. texture->view.scale.y = 1.0f;
  1359. texture->view.logical_scale.x = 1.0f;
  1360. texture->view.logical_scale.y = 1.0f;
  1361. texture->view.current_scale.x = 1.0f;
  1362. texture->view.current_scale.y = 1.0f;
  1363. texture->renderer = renderer;
  1364. texture->next = renderer->textures;
  1365. if (renderer->textures) {
  1366. renderer->textures->prev = texture;
  1367. }
  1368. renderer->textures = texture;
  1369. UpdatePixelViewport(renderer, &texture->view);
  1370. UpdatePixelClipRect(renderer, &texture->view);
  1371. texture->SDR_white_point = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDL_GetDefaultSDRWhitePoint(texture->colorspace));
  1372. texture->HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, SDL_GetDefaultHDRHeadroom(texture->colorspace));
  1373. // FOURCC format cannot be used directly by renderer back-ends for target texture
  1374. texture_is_fourcc_and_target = (access == SDL_TEXTUREACCESS_TARGET && SDL_ISPIXELFORMAT_FOURCC(format));
  1375. if (!texture_is_fourcc_and_target && IsSupportedFormat(renderer, format)) {
  1376. if (!renderer->CreateTexture(renderer, texture, props)) {
  1377. SDL_DestroyTexture(texture);
  1378. return NULL;
  1379. }
  1380. } else {
  1381. SDL_PixelFormat closest_format;
  1382. SDL_PropertiesID native_props = SDL_CreateProperties();
  1383. if (!texture_is_fourcc_and_target) {
  1384. closest_format = GetClosestSupportedFormat(renderer, format, false);
  1385. } else {
  1386. closest_format = renderer->texture_formats[0];
  1387. }
  1388. if (format == SDL_PIXELFORMAT_MJPG && closest_format == SDL_PIXELFORMAT_NV12) {
  1389. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, SDL_COLORSPACE_JPEG);
  1390. } else {
  1391. default_colorspace = SDL_GetDefaultColorspaceForFormat(closest_format);
  1392. if (SDL_COLORSPACETYPE(texture->colorspace) == SDL_COLORSPACETYPE(default_colorspace) &&
  1393. SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_COLORSPACETRANSFER(default_colorspace)) {
  1394. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture->colorspace);
  1395. } else {
  1396. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace);
  1397. }
  1398. }
  1399. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, closest_format);
  1400. if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
  1401. // We're going to be uploading pixels frequently as the palette changes
  1402. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING);
  1403. } else {
  1404. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, texture->access);
  1405. }
  1406. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, texture->w);
  1407. SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, texture->h);
  1408. texture->native = SDL_CreateTextureWithProperties(renderer, native_props);
  1409. SDL_DestroyProperties(native_props);
  1410. if (!texture->native) {
  1411. SDL_DestroyTexture(texture);
  1412. return NULL;
  1413. }
  1414. SDL_SetPointerProperty(SDL_GetTextureProperties(texture->native), SDL_PROP_TEXTURE_PARENT_POINTER, texture);
  1415. // Swap textures to have texture before texture->native in the list
  1416. texture->native->next = texture->next;
  1417. if (texture->native->next) {
  1418. texture->native->next->prev = texture->native;
  1419. }
  1420. texture->prev = texture->native->prev;
  1421. if (texture->prev) {
  1422. texture->prev->next = texture;
  1423. }
  1424. texture->native->prev = texture;
  1425. texture->next = texture->native;
  1426. renderer->textures = texture;
  1427. SDL_SetTextureScaleMode(texture->native, texture->scaleMode);
  1428. if (texture->format == SDL_PIXELFORMAT_MJPG) {
  1429. // We have a custom decode + upload path for this
  1430. } else if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
  1431. #ifdef SDL_HAVE_YUV
  1432. texture->yuv = SDL_SW_CreateYUVTexture(texture->format, texture->colorspace, w, h);
  1433. #else
  1434. SDL_SetError("SDL not built with YUV support");
  1435. #endif
  1436. if (!texture->yuv) {
  1437. SDL_DestroyTexture(texture);
  1438. return NULL;
  1439. }
  1440. } else if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
  1441. texture->palette_surface = SDL_CreateSurfaceZeroed(w, h, texture->format);
  1442. if (!texture->palette_surface) {
  1443. SDL_DestroyTexture(texture);
  1444. return NULL;
  1445. }
  1446. } else if (access == SDL_TEXTUREACCESS_STREAMING) {
  1447. // The pitch is 4 byte aligned
  1448. texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
  1449. texture->pixels = SDL_calloc(1, (size_t)texture->pitch * h);
  1450. if (!texture->pixels) {
  1451. SDL_DestroyTexture(texture);
  1452. return NULL;
  1453. }
  1454. }
  1455. }
  1456. if (SDL_ISPIXELFORMAT_INDEXED(texture->format) && palette) {
  1457. SDL_SetTexturePalette(texture, palette);
  1458. }
  1459. // Now set the properties for the new texture
  1460. props = SDL_GetTextureProperties(texture);
  1461. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace);
  1462. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, texture->format);
  1463. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, texture->access);
  1464. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_WIDTH_NUMBER, texture->w);
  1465. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_HEIGHT_NUMBER, texture->h);
  1466. SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT, texture->SDR_white_point);
  1467. if (texture->HDR_headroom > 0.0f) {
  1468. SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT, texture->HDR_headroom);
  1469. }
  1470. return texture;
  1471. }
  1472. SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, SDL_TextureAccess access, int w, int h)
  1473. {
  1474. SDL_Texture *texture;
  1475. SDL_PropertiesID props = SDL_CreateProperties();
  1476. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
  1477. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access);
  1478. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, w);
  1479. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, h);
  1480. texture = SDL_CreateTextureWithProperties(renderer, props);
  1481. SDL_DestroyProperties(props);
  1482. return texture;
  1483. }
  1484. static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, SDL_Surface *surface)
  1485. {
  1486. bool direct_update;
  1487. if (surface->format == texture->format &&
  1488. SDL_GetSurfaceColorspace(surface) == texture->colorspace) {
  1489. if (SDL_ISPIXELFORMAT_INDEXED(surface->format)) {
  1490. // Update Texture directly - the color key is handled later
  1491. direct_update = true;
  1492. } else if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
  1493. /* Surface and Renderer formats are identical.
  1494. * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
  1495. direct_update = false;
  1496. } else {
  1497. // Update Texture directly
  1498. direct_update = true;
  1499. }
  1500. } else {
  1501. // Surface and Renderer formats are different, it needs an intermediate conversion.
  1502. direct_update = false;
  1503. }
  1504. if (direct_update) {
  1505. if (SDL_MUSTLOCK(surface)) {
  1506. if (SDL_LockSurface(surface)) {
  1507. SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch);
  1508. SDL_UnlockSurface(surface);
  1509. }
  1510. } else {
  1511. SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch);
  1512. }
  1513. } else {
  1514. // Set up a destination surface for the texture update
  1515. SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->public_palette, texture->colorspace, SDL_GetSurfaceProperties(surface));
  1516. if (temp) {
  1517. SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
  1518. SDL_DestroySurface(temp);
  1519. } else {
  1520. return false;
  1521. }
  1522. }
  1523. if (SDL_ISPIXELFORMAT_INDEXED(texture->format) && SDL_SurfaceHasColorKey(surface)) {
  1524. Uint32 key;
  1525. SDL_Color col;
  1526. SDL_Palette *palette;
  1527. palette = SDL_GetTexturePalette(texture);
  1528. if (!palette || !SDL_GetSurfaceColorKey(surface, &key) || key >= (Uint32)palette->ncolors) {
  1529. return false;
  1530. }
  1531. col = palette->colors[key];
  1532. col.a = SDL_ALPHA_TRANSPARENT;
  1533. SDL_SetPaletteColors(palette, &col, key, 1);
  1534. }
  1535. {
  1536. Uint8 r, g, b, a;
  1537. SDL_BlendMode blendMode;
  1538. SDL_GetSurfaceColorMod(surface, &r, &g, &b);
  1539. SDL_SetTextureColorMod(texture, r, g, b);
  1540. SDL_GetSurfaceAlphaMod(surface, &a);
  1541. SDL_SetTextureAlphaMod(texture, a);
  1542. if (SDL_SurfaceHasColorKey(surface)) {
  1543. // We converted to a texture with alpha format
  1544. SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
  1545. } else {
  1546. SDL_GetSurfaceBlendMode(surface, &blendMode);
  1547. SDL_SetTextureBlendMode(texture, blendMode);
  1548. }
  1549. }
  1550. return true;
  1551. }
  1552. SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface)
  1553. {
  1554. int i;
  1555. SDL_PixelFormat format = SDL_PIXELFORMAT_UNKNOWN;
  1556. SDL_Texture *texture;
  1557. SDL_PropertiesID props;
  1558. SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN;
  1559. SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN;
  1560. CHECK_RENDERER_MAGIC(renderer, NULL);
  1561. CHECK_PARAM(!SDL_SurfaceValid(surface)) {
  1562. SDL_InvalidParamError("SDL_CreateTextureFromSurface(): surface");
  1563. return NULL;
  1564. }
  1565. // Try to have the best pixel format for the texture
  1566. // No alpha, but a colorkey => promote to alpha
  1567. if (!SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
  1568. if (surface->format == SDL_PIXELFORMAT_XRGB8888) {
  1569. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1570. if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) {
  1571. format = SDL_PIXELFORMAT_ARGB8888;
  1572. break;
  1573. }
  1574. }
  1575. } else if (surface->format == SDL_PIXELFORMAT_XBGR8888) {
  1576. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1577. if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) {
  1578. format = SDL_PIXELFORMAT_ABGR8888;
  1579. break;
  1580. }
  1581. }
  1582. } else if (surface->format == SDL_PIXELFORMAT_XRGB4444) {
  1583. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1584. if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ARGB4444) {
  1585. format = SDL_PIXELFORMAT_ARGB4444;
  1586. break;
  1587. }
  1588. }
  1589. }
  1590. } else {
  1591. // Exact match would be fine
  1592. for (i = 0; i < renderer->num_texture_formats; ++i) {
  1593. if (renderer->texture_formats[i] == surface->format) {
  1594. format = surface->format;
  1595. break;
  1596. }
  1597. }
  1598. }
  1599. // Fallback, choose a valid pixel format
  1600. if (format == SDL_PIXELFORMAT_UNKNOWN) {
  1601. bool needAlpha = SDL_SurfaceHasColorKey(surface);
  1602. // If palette contains alpha values, promotes to alpha format
  1603. if (surface->palette) {
  1604. bool is_opaque, has_alpha_channel;
  1605. SDL_DetectPalette(surface->palette, &is_opaque, &has_alpha_channel);
  1606. if (!is_opaque) {
  1607. needAlpha = true;
  1608. }
  1609. }
  1610. format = GetClosestSupportedFormat(renderer, surface->format, needAlpha);
  1611. }
  1612. surface_colorspace = SDL_GetSurfaceColorspace(surface);
  1613. texture_colorspace = surface_colorspace;
  1614. if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
  1615. SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
  1616. if (SDL_ISPIXELFORMAT_FLOAT(format)) {
  1617. texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR;
  1618. } else if (SDL_ISPIXELFORMAT_10BIT(format)) {
  1619. texture_colorspace = SDL_COLORSPACE_HDR10;
  1620. } else {
  1621. texture_colorspace = SDL_COLORSPACE_SRGB;
  1622. }
  1623. }
  1624. props = SDL_CreateProperties();
  1625. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture_colorspace);
  1626. if (surface_colorspace == texture_colorspace) {
  1627. SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT,
  1628. SDL_GetSurfaceSDRWhitePoint(surface, surface_colorspace));
  1629. }
  1630. SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT,
  1631. SDL_GetSurfaceHDRHeadroom(surface, surface_colorspace));
  1632. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
  1633. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
  1634. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w);
  1635. SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, surface->h);
  1636. SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER, surface->palette);
  1637. texture = SDL_CreateTextureWithProperties(renderer, props);
  1638. SDL_DestroyProperties(props);
  1639. if (!texture) {
  1640. return NULL;
  1641. }
  1642. if (!SDL_UpdateTextureFromSurface(texture, NULL, surface)) {
  1643. SDL_DestroyTexture(texture);
  1644. return NULL;
  1645. }
  1646. return texture;
  1647. }
  1648. SDL_Renderer *SDL_GetRendererFromTexture(SDL_Texture *texture)
  1649. {
  1650. CHECK_TEXTURE_MAGIC(texture, NULL);
  1651. return texture->renderer;
  1652. }
  1653. SDL_PropertiesID SDL_GetTextureProperties(SDL_Texture *texture)
  1654. {
  1655. CHECK_TEXTURE_MAGIC(texture, 0);
  1656. if (texture->props == 0) {
  1657. texture->props = SDL_CreateProperties();
  1658. }
  1659. return texture->props;
  1660. }
  1661. bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h)
  1662. {
  1663. if (w) {
  1664. *w = 0;
  1665. }
  1666. if (h) {
  1667. *h = 0;
  1668. }
  1669. CHECK_TEXTURE_MAGIC(texture, false);
  1670. if (w) {
  1671. *w = (float)texture->w;
  1672. }
  1673. if (h) {
  1674. *h = (float)texture->h;
  1675. }
  1676. return true;
  1677. }
  1678. bool SDL_SetTexturePalette(SDL_Texture *texture, SDL_Palette *palette)
  1679. {
  1680. CHECK_TEXTURE_MAGIC(texture, false);
  1681. CHECK_PARAM(!SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
  1682. return SDL_SetError("Texture isn't palettized format");
  1683. }
  1684. CHECK_PARAM(palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(texture->format))) {
  1685. return SDL_SetError("Palette doesn't match surface format");
  1686. }
  1687. if (palette != texture->public_palette) {
  1688. SDL_Renderer *renderer = texture->renderer;
  1689. if (texture->public_palette) {
  1690. SDL_DestroyPalette(texture->public_palette);
  1691. if (!texture->native) {
  1692. // Clean up the texture palette
  1693. --texture->palette->refcount;
  1694. if (texture->palette->refcount == 0) {
  1695. FlushRenderCommandsIfPaletteNeeded(renderer, texture->palette);
  1696. renderer->DestroyPalette(renderer, texture->palette);
  1697. SDL_RemoveFromHashTable(renderer->palettes, texture->public_palette);
  1698. }
  1699. texture->palette = NULL;
  1700. }
  1701. }
  1702. texture->public_palette = palette;
  1703. texture->palette_version = 0;
  1704. if (texture->public_palette) {
  1705. ++texture->public_palette->refcount;
  1706. if (!texture->native) {
  1707. if (SDL_FindInHashTable(renderer->palettes, palette, (const void **)&texture->palette)) {
  1708. ++texture->palette->refcount;
  1709. } else {
  1710. SDL_TexturePalette *texture_palette = (SDL_TexturePalette *)SDL_calloc(1, sizeof(*texture_palette));
  1711. if (!texture_palette) {
  1712. SDL_SetTexturePalette(texture, NULL);
  1713. return false;
  1714. }
  1715. if (!renderer->CreatePalette(renderer, texture_palette)) {
  1716. renderer->DestroyPalette(renderer, texture_palette);
  1717. SDL_SetTexturePalette(texture, NULL);
  1718. return false;
  1719. }
  1720. texture->palette = texture_palette;
  1721. texture->palette->refcount = 1;
  1722. if (!SDL_InsertIntoHashTable(renderer->palettes, palette, texture->palette, false)) {
  1723. SDL_SetTexturePalette(texture, NULL);
  1724. return false;
  1725. }
  1726. }
  1727. }
  1728. if (!texture->native && renderer->ChangeTexturePalette) {
  1729. renderer->ChangeTexturePalette(renderer, texture);
  1730. }
  1731. }
  1732. if (texture->palette_surface) {
  1733. SDL_SetSurfacePalette(texture->palette_surface, palette);
  1734. }
  1735. }
  1736. return true;
  1737. }
  1738. SDL_Palette *SDL_GetTexturePalette(SDL_Texture *texture)
  1739. {
  1740. CHECK_TEXTURE_MAGIC(texture, NULL);
  1741. return texture->public_palette;
  1742. }
  1743. bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b)
  1744. {
  1745. const float fR = (float)r / 255.0f;
  1746. const float fG = (float)g / 255.0f;
  1747. const float fB = (float)b / 255.0f;
  1748. return SDL_SetTextureColorModFloat(texture, fR, fG, fB);
  1749. }
  1750. bool SDL_SetTextureColorModFloat(SDL_Texture *texture, float r, float g, float b)
  1751. {
  1752. CHECK_TEXTURE_MAGIC(texture, false);
  1753. texture->color.r = r;
  1754. texture->color.g = g;
  1755. texture->color.b = b;
  1756. if (texture->native) {
  1757. return SDL_SetTextureColorModFloat(texture->native, r, g, b);
  1758. }
  1759. return true;
  1760. }
  1761. bool SDL_GetTextureColorMod(SDL_Texture *texture, Uint8 *r, Uint8 *g, Uint8 *b)
  1762. {
  1763. float fR = 1.0f, fG = 1.0f, fB = 1.0f;
  1764. if (!SDL_GetTextureColorModFloat(texture, &fR, &fG, &fB)) {
  1765. if (r) {
  1766. *r = 255;
  1767. }
  1768. if (g) {
  1769. *g = 255;
  1770. }
  1771. if (b) {
  1772. *b = 255;
  1773. }
  1774. return false;
  1775. }
  1776. if (r) {
  1777. *r = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f);
  1778. }
  1779. if (g) {
  1780. *g = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f);
  1781. }
  1782. if (b) {
  1783. *b = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f);
  1784. }
  1785. return true;
  1786. }
  1787. bool SDL_GetTextureColorModFloat(SDL_Texture *texture, float *r, float *g, float *b)
  1788. {
  1789. SDL_FColor color;
  1790. if (r) {
  1791. *r = 1.0f;
  1792. }
  1793. if (g) {
  1794. *g = 1.0f;
  1795. }
  1796. if (b) {
  1797. *b = 1.0f;
  1798. }
  1799. CHECK_TEXTURE_MAGIC(texture, false);
  1800. color = texture->color;
  1801. if (r) {
  1802. *r = color.r;
  1803. }
  1804. if (g) {
  1805. *g = color.g;
  1806. }
  1807. if (b) {
  1808. *b = color.b;
  1809. }
  1810. return true;
  1811. }
  1812. bool SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha)
  1813. {
  1814. const float fA = (float)alpha / 255.0f;
  1815. return SDL_SetTextureAlphaModFloat(texture, fA);
  1816. }
  1817. bool SDL_SetTextureAlphaModFloat(SDL_Texture *texture, float alpha)
  1818. {
  1819. CHECK_TEXTURE_MAGIC(texture, false);
  1820. texture->color.a = alpha;
  1821. if (texture->native) {
  1822. return SDL_SetTextureAlphaModFloat(texture->native, alpha);
  1823. }
  1824. return true;
  1825. }
  1826. bool SDL_GetTextureAlphaMod(SDL_Texture *texture, Uint8 *alpha)
  1827. {
  1828. float fA = 1.0f;
  1829. if (!SDL_GetTextureAlphaModFloat(texture, &fA)) {
  1830. if (alpha) {
  1831. *alpha = 255;
  1832. }
  1833. return false;
  1834. }
  1835. if (alpha) {
  1836. *alpha = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f);
  1837. }
  1838. return true;
  1839. }
  1840. bool SDL_GetTextureAlphaModFloat(SDL_Texture *texture, float *alpha)
  1841. {
  1842. if (alpha) {
  1843. *alpha = 1.0f;
  1844. }
  1845. CHECK_TEXTURE_MAGIC(texture, false);
  1846. if (alpha) {
  1847. *alpha = texture->color.a;
  1848. }
  1849. return true;
  1850. }
  1851. bool SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode)
  1852. {
  1853. SDL_Renderer *renderer;
  1854. CHECK_TEXTURE_MAGIC(texture, false);
  1855. CHECK_PARAM(blendMode == SDL_BLENDMODE_INVALID) {
  1856. return SDL_InvalidParamError("blendMode");
  1857. }
  1858. renderer = texture->renderer;
  1859. if (!IsSupportedBlendMode(renderer, blendMode)) {
  1860. return SDL_Unsupported();
  1861. }
  1862. texture->blendMode = blendMode;
  1863. if (texture->native) {
  1864. return SDL_SetTextureBlendMode(texture->native, blendMode);
  1865. }
  1866. return true;
  1867. }
  1868. bool SDL_GetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode *blendMode)
  1869. {
  1870. if (blendMode) {
  1871. *blendMode = SDL_BLENDMODE_INVALID;
  1872. }
  1873. CHECK_TEXTURE_MAGIC(texture, false);
  1874. if (blendMode) {
  1875. *blendMode = texture->blendMode;
  1876. }
  1877. return true;
  1878. }
  1879. bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode)
  1880. {
  1881. CHECK_TEXTURE_MAGIC(texture, false);
  1882. switch (scaleMode) {
  1883. case SDL_SCALEMODE_NEAREST:
  1884. case SDL_SCALEMODE_PIXELART:
  1885. case SDL_SCALEMODE_LINEAR:
  1886. break;
  1887. default:
  1888. return SDL_InvalidParamError("scaleMode");
  1889. }
  1890. texture->scaleMode = scaleMode;
  1891. if (texture->native) {
  1892. return SDL_SetTextureScaleMode(texture->native, scaleMode);
  1893. }
  1894. return true;
  1895. }
  1896. bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode)
  1897. {
  1898. if (scaleMode) {
  1899. *scaleMode = SDL_SCALEMODE_INVALID;
  1900. }
  1901. CHECK_TEXTURE_MAGIC(texture, false);
  1902. if (scaleMode) {
  1903. *scaleMode = texture->scaleMode;
  1904. }
  1905. return true;
  1906. }
  1907. #ifdef SDL_HAVE_YUV
  1908. static bool SDL_UpdateTextureYUV(SDL_Texture *texture, const SDL_Rect *rect,
  1909. const void *pixels, int pitch)
  1910. {
  1911. SDL_Texture *native = texture->native;
  1912. SDL_Rect full_rect;
  1913. bool result = true;
  1914. if (!SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch)) {
  1915. return false;
  1916. }
  1917. full_rect.x = 0;
  1918. full_rect.y = 0;
  1919. full_rect.w = texture->w;
  1920. full_rect.h = texture->h;
  1921. rect = &full_rect;
  1922. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1923. // We can lock the texture and copy to it
  1924. void *native_pixels = NULL;
  1925. int native_pitch = 0;
  1926. if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) {
  1927. return false;
  1928. }
  1929. result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, native_pixels, native_pitch);
  1930. SDL_UnlockTexture(native);
  1931. } else {
  1932. // Use a temporary buffer for updating
  1933. const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1934. const size_t alloclen = (size_t)rect->h * temp_pitch;
  1935. if (alloclen > 0) {
  1936. void *temp_pixels = SDL_malloc(alloclen);
  1937. if (!temp_pixels) {
  1938. return false;
  1939. }
  1940. result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, temp_pixels, temp_pitch);
  1941. if (result) {
  1942. SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1943. }
  1944. SDL_free(temp_pixels);
  1945. }
  1946. }
  1947. return result;
  1948. }
  1949. #endif // SDL_HAVE_YUV
  1950. static bool SDL_UpdateTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
  1951. {
  1952. SDL_Surface *surface = texture->palette_surface;
  1953. const Uint8 *src = (const Uint8 *)pixels;
  1954. Uint8 *dst = ((Uint8 *)surface->pixels) + rect->y * surface->pitch + (rect->x * SDL_BITSPERPIXEL(texture->format)) / 8;
  1955. int w = ((rect->w * SDL_BITSPERPIXEL(texture->format)) + 7) / 8;
  1956. int h = rect->h;
  1957. while (h--) {
  1958. SDL_memcpy(dst, src, w);
  1959. src += pitch;
  1960. dst += surface->pitch;
  1961. }
  1962. texture->palette_version = 0;
  1963. return true;
  1964. }
  1965. static bool SDL_UpdateTextureNative(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
  1966. {
  1967. SDL_Texture *native = texture->native;
  1968. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1969. // We can lock the texture and copy to it
  1970. void *native_pixels = NULL;
  1971. int native_pitch = 0;
  1972. if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) {
  1973. return false;
  1974. }
  1975. SDL_ConvertPixelsAndColorspace(rect->w, rect->h,
  1976. texture->format, texture->colorspace, 0, pixels, pitch,
  1977. native->format, native->colorspace, 0, native_pixels, native_pitch);
  1978. SDL_UnlockTexture(native);
  1979. } else {
  1980. // Use a temporary buffer for updating
  1981. const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1982. const size_t alloclen = (size_t)rect->h * temp_pitch;
  1983. if (alloclen > 0) {
  1984. void *temp_pixels = SDL_malloc(alloclen);
  1985. if (!temp_pixels) {
  1986. return false;
  1987. }
  1988. SDL_ConvertPixelsAndColorspace(rect->w, rect->h,
  1989. texture->format, texture->colorspace, 0, pixels, pitch,
  1990. native->format, native->colorspace, 0, temp_pixels, temp_pitch);
  1991. SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1992. SDL_free(temp_pixels);
  1993. }
  1994. }
  1995. return true;
  1996. }
  1997. bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
  1998. {
  1999. SDL_Rect real_rect;
  2000. CHECK_TEXTURE_MAGIC(texture, false);
  2001. CHECK_PARAM(!pixels) {
  2002. return SDL_InvalidParamError("pixels");
  2003. }
  2004. CHECK_PARAM(!pitch) {
  2005. return SDL_InvalidParamError("pitch");
  2006. }
  2007. real_rect.x = 0;
  2008. real_rect.y = 0;
  2009. real_rect.w = texture->w;
  2010. real_rect.h = texture->h;
  2011. if (rect) {
  2012. if (!SDL_GetRectIntersection(rect, &real_rect, &real_rect)) {
  2013. return true;
  2014. }
  2015. }
  2016. if (real_rect.w == 0 || real_rect.h == 0) {
  2017. return true; // nothing to do.
  2018. #ifdef SDL_HAVE_YUV
  2019. } else if (texture->yuv) {
  2020. return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch);
  2021. #endif
  2022. } else if (texture->palette_surface) {
  2023. return SDL_UpdateTexturePaletteSurface(texture, &real_rect, pixels, pitch);
  2024. } else if (texture->native) {
  2025. return SDL_UpdateTextureNative(texture, &real_rect, pixels, pitch);
  2026. } else {
  2027. SDL_Renderer *renderer = texture->renderer;
  2028. if (!FlushRenderCommandsIfTextureNeeded(texture)) {
  2029. return false;
  2030. }
  2031. return renderer->UpdateTexture(renderer, texture, &real_rect, pixels, pitch);
  2032. }
  2033. }
  2034. #ifdef SDL_HAVE_YUV
  2035. static bool SDL_UpdateTextureYUVPlanar(SDL_Texture *texture, const SDL_Rect *rect,
  2036. const Uint8 *Yplane, int Ypitch,
  2037. const Uint8 *Uplane, int Upitch,
  2038. const Uint8 *Vplane, int Vpitch)
  2039. {
  2040. SDL_Texture *native = texture->native;
  2041. SDL_Rect full_rect;
  2042. bool result = true;
  2043. if (!SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch)) {
  2044. return false;
  2045. }
  2046. full_rect.x = 0;
  2047. full_rect.y = 0;
  2048. full_rect.w = texture->w;
  2049. full_rect.h = texture->h;
  2050. rect = &full_rect;
  2051. if (!rect->w || !rect->h) {
  2052. return true; // nothing to do.
  2053. }
  2054. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  2055. // We can lock the texture and copy to it
  2056. void *native_pixels = NULL;
  2057. int native_pitch = 0;
  2058. if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) {
  2059. return false;
  2060. }
  2061. result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, native_pixels, native_pitch);
  2062. SDL_UnlockTexture(native);
  2063. } else {
  2064. // Use a temporary buffer for updating
  2065. const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  2066. const size_t alloclen = (size_t)rect->h * temp_pitch;
  2067. if (alloclen > 0) {
  2068. void *temp_pixels = SDL_malloc(alloclen);
  2069. if (!temp_pixels) {
  2070. return false;
  2071. }
  2072. result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, temp_pixels, temp_pitch);
  2073. if (result) {
  2074. SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  2075. }
  2076. SDL_free(temp_pixels);
  2077. }
  2078. }
  2079. return result;
  2080. }
  2081. static bool SDL_UpdateTextureNVPlanar(SDL_Texture *texture, const SDL_Rect *rect,
  2082. const Uint8 *Yplane, int Ypitch,
  2083. const Uint8 *UVplane, int UVpitch)
  2084. {
  2085. SDL_Texture *native = texture->native;
  2086. SDL_Rect full_rect;
  2087. bool result = true;
  2088. if (!SDL_SW_UpdateNVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, UVplane, UVpitch)) {
  2089. return false;
  2090. }
  2091. full_rect.x = 0;
  2092. full_rect.y = 0;
  2093. full_rect.w = texture->w;
  2094. full_rect.h = texture->h;
  2095. rect = &full_rect;
  2096. if (!rect->w || !rect->h) {
  2097. return true; // nothing to do.
  2098. }
  2099. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  2100. // We can lock the texture and copy to it
  2101. void *native_pixels = NULL;
  2102. int native_pitch = 0;
  2103. if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) {
  2104. return false;
  2105. }
  2106. result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, native_pixels, native_pitch);
  2107. SDL_UnlockTexture(native);
  2108. } else {
  2109. // Use a temporary buffer for updating
  2110. const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  2111. const size_t alloclen = (size_t)rect->h * temp_pitch;
  2112. if (alloclen > 0) {
  2113. void *temp_pixels = SDL_malloc(alloclen);
  2114. if (!temp_pixels) {
  2115. return false;
  2116. }
  2117. result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, temp_pixels, temp_pitch);
  2118. if (result) {
  2119. SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  2120. }
  2121. SDL_free(temp_pixels);
  2122. }
  2123. }
  2124. return result;
  2125. }
  2126. #endif // SDL_HAVE_YUV
  2127. bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect,
  2128. const Uint8 *Yplane, int Ypitch,
  2129. const Uint8 *Uplane, int Upitch,
  2130. const Uint8 *Vplane, int Vpitch)
  2131. {
  2132. SDL_Renderer *renderer;
  2133. SDL_Rect real_rect;
  2134. CHECK_TEXTURE_MAGIC(texture, false);
  2135. CHECK_PARAM(!Yplane) {
  2136. return SDL_InvalidParamError("Yplane");
  2137. }
  2138. CHECK_PARAM(!Ypitch) {
  2139. return SDL_InvalidParamError("Ypitch");
  2140. }
  2141. CHECK_PARAM(!Uplane) {
  2142. return SDL_InvalidParamError("Uplane");
  2143. }
  2144. CHECK_PARAM(!Upitch) {
  2145. return SDL_InvalidParamError("Upitch");
  2146. }
  2147. CHECK_PARAM(!Vplane) {
  2148. return SDL_InvalidParamError("Vplane");
  2149. }
  2150. CHECK_PARAM(!Vpitch) {
  2151. return SDL_InvalidParamError("Vpitch");
  2152. }
  2153. CHECK_PARAM(texture->format != SDL_PIXELFORMAT_YV12 &&
  2154. texture->format != SDL_PIXELFORMAT_IYUV &&
  2155. texture->format != SDL_PIXELFORMAT_I444 &&
  2156. texture->format != SDL_PIXELFORMAT_I0FL &&
  2157. texture->format != SDL_PIXELFORMAT_I4FL) {
  2158. return SDL_SetError("Texture format must be YV12, IYUV, I444, I0FL, or I4FL");
  2159. }
  2160. real_rect.x = 0;
  2161. real_rect.y = 0;
  2162. real_rect.w = texture->w;
  2163. real_rect.h = texture->h;
  2164. if (rect) {
  2165. SDL_GetRectIntersection(rect, &real_rect, &real_rect);
  2166. }
  2167. if (real_rect.w == 0 || real_rect.h == 0) {
  2168. return true; // nothing to do.
  2169. }
  2170. #ifdef SDL_HAVE_YUV
  2171. if (texture->yuv) {
  2172. return SDL_UpdateTextureYUVPlanar(texture, &real_rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
  2173. } else
  2174. #endif
  2175. {
  2176. SDL_assert(!texture->native);
  2177. renderer = texture->renderer;
  2178. SDL_assert(renderer->UpdateTextureYUV);
  2179. if (renderer->UpdateTextureYUV) {
  2180. if (!FlushRenderCommandsIfTextureNeeded(texture)) {
  2181. return false;
  2182. }
  2183. return renderer->UpdateTextureYUV(renderer, texture, &real_rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
  2184. } else {
  2185. return SDL_Unsupported();
  2186. }
  2187. }
  2188. }
  2189. bool SDL_UpdateNVTexture(SDL_Texture *texture, const SDL_Rect *rect,
  2190. const Uint8 *Yplane, int Ypitch,
  2191. const Uint8 *UVplane, int UVpitch)
  2192. {
  2193. SDL_Renderer *renderer;
  2194. SDL_Rect real_rect;
  2195. CHECK_TEXTURE_MAGIC(texture, false);
  2196. CHECK_PARAM(!Yplane) {
  2197. return SDL_InvalidParamError("Yplane");
  2198. }
  2199. CHECK_PARAM(!Ypitch) {
  2200. return SDL_InvalidParamError("Ypitch");
  2201. }
  2202. CHECK_PARAM(!UVplane) {
  2203. return SDL_InvalidParamError("UVplane");
  2204. }
  2205. CHECK_PARAM(!UVpitch) {
  2206. return SDL_InvalidParamError("UVpitch");
  2207. }
  2208. CHECK_PARAM(texture->format != SDL_PIXELFORMAT_NV12 &&
  2209. texture->format != SDL_PIXELFORMAT_NV21 &&
  2210. texture->format != SDL_PIXELFORMAT_P010) {
  2211. return SDL_SetError("Texture format must be NV12, NV21, or P010");
  2212. }
  2213. real_rect.x = 0;
  2214. real_rect.y = 0;
  2215. real_rect.w = texture->w;
  2216. real_rect.h = texture->h;
  2217. if (rect) {
  2218. SDL_GetRectIntersection(rect, &real_rect, &real_rect);
  2219. }
  2220. if (real_rect.w == 0 || real_rect.h == 0) {
  2221. return true; // nothing to do.
  2222. }
  2223. #ifdef SDL_HAVE_YUV
  2224. if (texture->yuv) {
  2225. return SDL_UpdateTextureNVPlanar(texture, &real_rect, Yplane, Ypitch, UVplane, UVpitch);
  2226. } else
  2227. #endif
  2228. {
  2229. SDL_assert(!texture->native);
  2230. renderer = texture->renderer;
  2231. SDL_assert(renderer->UpdateTextureNV);
  2232. if (renderer->UpdateTextureNV) {
  2233. if (!FlushRenderCommandsIfTextureNeeded(texture)) {
  2234. return false;
  2235. }
  2236. return renderer->UpdateTextureNV(renderer, texture, &real_rect, Yplane, Ypitch, UVplane, UVpitch);
  2237. } else {
  2238. return SDL_Unsupported();
  2239. }
  2240. }
  2241. }
  2242. #ifdef SDL_HAVE_YUV
  2243. static bool SDL_LockTextureYUV(SDL_Texture *texture, const SDL_Rect *rect,
  2244. void **pixels, int *pitch)
  2245. {
  2246. return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
  2247. }
  2248. #endif // SDL_HAVE_YUV
  2249. static bool SDL_LockTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch)
  2250. {
  2251. SDL_Surface *surface = texture->palette_surface;
  2252. *pixels = ((Uint8 *)surface->pixels) + rect->y * surface->pitch + (rect->x * SDL_BITSPERPIXEL(texture->format)) / 8;
  2253. *pitch = surface->pitch;
  2254. return true;
  2255. }
  2256. static bool SDL_LockTextureNative(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch)
  2257. {
  2258. texture->locked_rect = *rect;
  2259. *pixels = (void *)((Uint8 *)texture->pixels +
  2260. rect->y * texture->pitch +
  2261. rect->x * SDL_BYTESPERPIXEL(texture->format));
  2262. *pitch = texture->pitch;
  2263. return true;
  2264. }
  2265. bool SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch)
  2266. {
  2267. SDL_Rect full_rect;
  2268. CHECK_TEXTURE_MAGIC(texture, false);
  2269. CHECK_PARAM(texture->access != SDL_TEXTUREACCESS_STREAMING) {
  2270. return SDL_SetError("SDL_LockTexture(): texture must be streaming");
  2271. }
  2272. if (!rect) {
  2273. full_rect.x = 0;
  2274. full_rect.y = 0;
  2275. full_rect.w = texture->w;
  2276. full_rect.h = texture->h;
  2277. rect = &full_rect;
  2278. }
  2279. #ifdef SDL_HAVE_YUV
  2280. if (texture->yuv) {
  2281. if (!FlushRenderCommandsIfTextureNeeded(texture)) {
  2282. return false;
  2283. }
  2284. return SDL_LockTextureYUV(texture, rect, pixels, pitch);
  2285. } else
  2286. #endif
  2287. if (texture->palette_surface) {
  2288. return SDL_LockTexturePaletteSurface(texture, rect, pixels, pitch);
  2289. } else if (texture->native) {
  2290. // Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then.
  2291. return SDL_LockTextureNative(texture, rect, pixels, pitch);
  2292. } else {
  2293. SDL_Renderer *renderer = texture->renderer;
  2294. if (!FlushRenderCommandsIfTextureNeeded(texture)) {
  2295. return false;
  2296. }
  2297. return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
  2298. }
  2299. }
  2300. bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Surface **surface)
  2301. {
  2302. SDL_Rect real_rect;
  2303. void *pixels = NULL;
  2304. int pitch = 0; // fix static analysis
  2305. CHECK_TEXTURE_MAGIC(texture, false);
  2306. CHECK_PARAM(!surface) {
  2307. return SDL_InvalidParamError("surface");
  2308. }
  2309. real_rect.x = 0;
  2310. real_rect.y = 0;
  2311. real_rect.w = texture->w;
  2312. real_rect.h = texture->h;
  2313. if (rect) {
  2314. SDL_GetRectIntersection(rect, &real_rect, &real_rect);
  2315. }
  2316. if (!SDL_LockTexture(texture, &real_rect, &pixels, &pitch)) {
  2317. return false;
  2318. }
  2319. texture->locked_surface = SDL_CreateSurfaceFrom(real_rect.w, real_rect.h, texture->format, pixels, pitch);
  2320. if (!texture->locked_surface) {
  2321. SDL_UnlockTexture(texture);
  2322. return false;
  2323. }
  2324. if (texture->public_palette) {
  2325. SDL_SetSurfacePalette(texture->locked_surface, texture->public_palette);
  2326. }
  2327. *surface = texture->locked_surface;
  2328. return true;
  2329. }
  2330. #ifdef SDL_HAVE_YUV
  2331. static void SDL_UnlockTextureYUV(SDL_Texture *texture)
  2332. {
  2333. SDL_Texture *native = texture->native;
  2334. void *native_pixels = NULL;
  2335. int native_pitch = 0;
  2336. SDL_Rect rect;
  2337. rect.x = 0;
  2338. rect.y = 0;
  2339. rect.w = texture->w;
  2340. rect.h = texture->h;
  2341. if (!SDL_LockTexture(native, &rect, &native_pixels, &native_pitch)) {
  2342. return;
  2343. }
  2344. SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
  2345. rect.w, rect.h, native_pixels, native_pitch);
  2346. SDL_UnlockTexture(native);
  2347. }
  2348. #endif // SDL_HAVE_YUV
  2349. static void SDL_UnlockTexturePaletteSurface(SDL_Texture *texture)
  2350. {
  2351. texture->palette_version = 0;
  2352. }
  2353. static void SDL_UnlockTextureNative(SDL_Texture *texture)
  2354. {
  2355. SDL_Texture *native = texture->native;
  2356. void *native_pixels = NULL;
  2357. int native_pitch = 0;
  2358. const SDL_Rect *rect = &texture->locked_rect;
  2359. const void *pixels = (void *)((Uint8 *)texture->pixels +
  2360. rect->y * texture->pitch +
  2361. rect->x * SDL_BYTESPERPIXEL(texture->format));
  2362. int pitch = texture->pitch;
  2363. if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) {
  2364. return;
  2365. }
  2366. SDL_ConvertPixels(rect->w, rect->h,
  2367. texture->format, pixels, pitch,
  2368. native->format, native_pixels, native_pitch);
  2369. SDL_UnlockTexture(native);
  2370. }
  2371. void SDL_UnlockTexture(SDL_Texture *texture)
  2372. {
  2373. CHECK_TEXTURE_MAGIC(texture,);
  2374. if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
  2375. return;
  2376. }
  2377. #ifdef SDL_HAVE_YUV
  2378. if (texture->yuv) {
  2379. SDL_UnlockTextureYUV(texture);
  2380. } else
  2381. #endif
  2382. if (texture->palette_surface) {
  2383. SDL_UnlockTexturePaletteSurface(texture);
  2384. } else if (texture->native) {
  2385. SDL_UnlockTextureNative(texture);
  2386. } else {
  2387. SDL_Renderer *renderer = texture->renderer;
  2388. renderer->UnlockTexture(renderer, texture);
  2389. }
  2390. if (texture->locked_surface) {
  2391. SDL_DestroySurface(texture->locked_surface);
  2392. texture->locked_surface = NULL;
  2393. }
  2394. }
  2395. bool SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
  2396. {
  2397. CHECK_RENDERER_MAGIC(renderer, false);
  2398. // texture == NULL is valid and means reset the target to the window
  2399. if (texture) {
  2400. CHECK_TEXTURE_MAGIC(texture, false);
  2401. CHECK_PARAM(renderer != texture->renderer) {
  2402. return SDL_SetError("Texture was not created with this renderer");
  2403. }
  2404. CHECK_PARAM(texture->access != SDL_TEXTUREACCESS_TARGET) {
  2405. return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
  2406. }
  2407. if (texture->native) {
  2408. // Always render to the native texture
  2409. texture = texture->native;
  2410. }
  2411. }
  2412. if (texture == renderer->target) {
  2413. // Nothing to do!
  2414. return true;
  2415. }
  2416. FlushRenderCommands(renderer); // time to send everything to the GPU!
  2417. SDL_LockMutex(renderer->target_mutex);
  2418. renderer->target = texture;
  2419. if (texture) {
  2420. renderer->view = &texture->view;
  2421. renderer->current_colorspace = texture->colorspace;
  2422. } else {
  2423. renderer->view = &renderer->main_view;
  2424. renderer->current_colorspace = renderer->output_colorspace;
  2425. }
  2426. UpdateColorScale(renderer);
  2427. if (!renderer->SetRenderTarget(renderer, texture)) {
  2428. SDL_UnlockMutex(renderer->target_mutex);
  2429. return false;
  2430. }
  2431. SDL_UnlockMutex(renderer->target_mutex);
  2432. if (!QueueCmdSetViewport(renderer)) {
  2433. return false;
  2434. }
  2435. if (!QueueCmdSetClipRect(renderer)) {
  2436. return false;
  2437. }
  2438. // All set!
  2439. return true;
  2440. }
  2441. SDL_Texture *SDL_GetRenderTarget(SDL_Renderer *renderer)
  2442. {
  2443. CHECK_RENDERER_MAGIC(renderer, NULL);
  2444. if (!renderer->target) {
  2445. return NULL;
  2446. }
  2447. return (SDL_Texture *) SDL_GetPointerProperty(SDL_GetTextureProperties(renderer->target), SDL_PROP_TEXTURE_PARENT_POINTER, renderer->target);
  2448. }
  2449. static void UpdateLogicalPresentation(SDL_Renderer *renderer)
  2450. {
  2451. SDL_RenderViewState *view = renderer->view;
  2452. const bool is_main_view = (view == &renderer->main_view);
  2453. const float logical_w = view->logical_w;
  2454. const float logical_h = view->logical_h;
  2455. int iwidth, iheight;
  2456. if (is_main_view) {
  2457. SDL_GetRenderOutputSize(renderer, &iwidth, &iheight);
  2458. } else {
  2459. SDL_assert(renderer->target != NULL);
  2460. iwidth = (int)renderer->target->w;
  2461. iheight = (int)renderer->target->h;
  2462. }
  2463. view->logical_src_rect.x = 0.0f;
  2464. view->logical_src_rect.y = 0.0f;
  2465. view->logical_src_rect.w = logical_w;
  2466. view->logical_src_rect.h = logical_h;
  2467. if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_DISABLED) {
  2468. view->logical_dst_rect.x = 0.0f;
  2469. view->logical_dst_rect.y = 0.0f;
  2470. view->logical_dst_rect.w = iwidth;
  2471. view->logical_dst_rect.h = iheight;
  2472. view->logical_offset.x = view->logical_offset.y = 0.0f;
  2473. view->logical_scale.x = view->logical_scale.y = 1.0f;
  2474. view->current_scale.x = view->scale.x; // skip the multiplications against 1.0f.
  2475. view->current_scale.y = view->scale.y;
  2476. } else {
  2477. const float output_w = (float)iwidth;
  2478. const float output_h = (float)iheight;
  2479. const float want_aspect = logical_w / logical_h;
  2480. const float real_aspect = output_w / output_h;
  2481. if ((logical_w <= 0.0f) || (logical_h <= 0.0f)) {
  2482. view->logical_dst_rect.x = 0.0f;
  2483. view->logical_dst_rect.y = 0.0f;
  2484. view->logical_dst_rect.w = output_w;
  2485. view->logical_dst_rect.h = output_h;
  2486. } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) {
  2487. float scale;
  2488. if (want_aspect > real_aspect) {
  2489. scale = (float)((int)output_w / (int)logical_w); // This an integer division!
  2490. } else {
  2491. scale = (float)((int)output_h / (int)logical_h); // This an integer division!
  2492. }
  2493. if (scale < 1.0f) {
  2494. scale = 1.0f;
  2495. }
  2496. view->logical_dst_rect.w = SDL_floorf(logical_w * scale);
  2497. view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f;
  2498. view->logical_dst_rect.h = SDL_floorf(logical_h * scale);
  2499. view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f;
  2500. } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_STRETCH || SDL_fabsf(want_aspect - real_aspect) < 0.0001f) {
  2501. view->logical_dst_rect.x = 0.0f;
  2502. view->logical_dst_rect.y = 0.0f;
  2503. view->logical_dst_rect.w = output_w;
  2504. view->logical_dst_rect.h = output_h;
  2505. } else if (want_aspect > real_aspect) {
  2506. if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) {
  2507. // We want a wider aspect ratio than is available - letterbox it
  2508. const float scale = output_w / logical_w;
  2509. view->logical_dst_rect.x = 0.0f;
  2510. view->logical_dst_rect.w = output_w;
  2511. view->logical_dst_rect.h = SDL_floorf(logical_h * scale);
  2512. view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f;
  2513. } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN
  2514. /* We want a wider aspect ratio than is available -
  2515. zoom so logical height matches the real height
  2516. and the width will grow off the screen
  2517. */
  2518. const float scale = output_h / logical_h;
  2519. view->logical_dst_rect.y = 0.0f;
  2520. view->logical_dst_rect.h = output_h;
  2521. view->logical_dst_rect.w = SDL_floorf(logical_w * scale);
  2522. view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f;
  2523. }
  2524. } else {
  2525. if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) {
  2526. // We want a narrower aspect ratio than is available - use side-bars
  2527. const float scale = output_h / logical_h;
  2528. view->logical_dst_rect.y = 0.0f;
  2529. view->logical_dst_rect.h = output_h;
  2530. view->logical_dst_rect.w = SDL_floorf(logical_w * scale);
  2531. view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f;
  2532. } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN
  2533. /* We want a narrower aspect ratio than is available -
  2534. zoom so logical width matches the real width
  2535. and the height will grow off the screen
  2536. */
  2537. const float scale = output_w / logical_w;
  2538. view->logical_dst_rect.x = 0.0f;
  2539. view->logical_dst_rect.w = output_w;
  2540. view->logical_dst_rect.h = SDL_floorf(logical_h * scale);
  2541. view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f;
  2542. }
  2543. }
  2544. view->logical_scale.x = (logical_w > 0.0f) ? view->logical_dst_rect.w / logical_w : 0.0f;
  2545. view->logical_scale.y = (logical_h > 0.0f) ? view->logical_dst_rect.h / logical_h : 0.0f;
  2546. view->current_scale.x = view->scale.x * view->logical_scale.x;
  2547. view->current_scale.y = view->scale.y * view->logical_scale.y;
  2548. view->logical_offset.x = view->logical_dst_rect.x;
  2549. view->logical_offset.y = view->logical_dst_rect.y;
  2550. }
  2551. if (is_main_view) {
  2552. // This makes sure the dpi_scale is right. It also sets pixel_w and pixel_h, but we're going to change them directly below here.
  2553. UpdateMainViewDimensions(renderer);
  2554. }
  2555. view->pixel_w = (int) view->logical_dst_rect.w;
  2556. view->pixel_h = (int) view->logical_dst_rect.h;
  2557. UpdatePixelViewport(renderer, view);
  2558. UpdatePixelClipRect(renderer, view);
  2559. QueueCmdSetViewport(renderer);
  2560. QueueCmdSetClipRect(renderer);
  2561. }
  2562. bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode)
  2563. {
  2564. CHECK_RENDERER_MAGIC(renderer, false);
  2565. SDL_RenderViewState *view = renderer->view;
  2566. if (mode == SDL_LOGICAL_PRESENTATION_DISABLED) {
  2567. view->logical_w = 0;
  2568. view->logical_h = 0;
  2569. } else {
  2570. view->logical_w = w;
  2571. view->logical_h = h;
  2572. }
  2573. view->logical_presentation_mode = mode;
  2574. UpdateLogicalPresentation(renderer);
  2575. return true;
  2576. }
  2577. bool SDL_GetRenderLogicalPresentation(SDL_Renderer *renderer, int *w, int *h, SDL_RendererLogicalPresentation *mode)
  2578. {
  2579. #define SETVAL(ptr, val) if (ptr) { *ptr = val; }
  2580. SETVAL(w, 0);
  2581. SETVAL(h, 0);
  2582. SETVAL(mode, SDL_LOGICAL_PRESENTATION_DISABLED);
  2583. CHECK_RENDERER_MAGIC(renderer, false);
  2584. const SDL_RenderViewState *view = renderer->view;
  2585. SETVAL(w, view->logical_w);
  2586. SETVAL(h, view->logical_h);
  2587. SETVAL(mode, view->logical_presentation_mode);
  2588. #undef SETVAL
  2589. return true;
  2590. }
  2591. bool SDL_GetRenderLogicalPresentationRect(SDL_Renderer *renderer, SDL_FRect *rect)
  2592. {
  2593. if (rect) {
  2594. SDL_zerop(rect);
  2595. }
  2596. CHECK_RENDERER_MAGIC(renderer, false);
  2597. if (rect) {
  2598. SDL_copyp(rect, &renderer->view->logical_dst_rect);
  2599. }
  2600. return true;
  2601. }
  2602. static bool SDL_RenderVectorFromWindow(SDL_Renderer *renderer, float window_dx, float window_dy, float *dx, float *dy)
  2603. {
  2604. // Convert from window coordinates to pixels within the window
  2605. window_dx *= renderer->dpi_scale.x;
  2606. window_dy *= renderer->dpi_scale.y;
  2607. // Convert from pixels within the window to pixels within the view
  2608. const SDL_RenderViewState *view = &renderer->main_view;
  2609. if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) {
  2610. const SDL_FRect *src = &view->logical_src_rect;
  2611. const SDL_FRect *dst = &view->logical_dst_rect;
  2612. window_dx = (window_dx * src->w) / dst->w;
  2613. window_dy = (window_dy * src->h) / dst->h;
  2614. }
  2615. window_dx /= view->scale.x;
  2616. window_dy /= view->scale.y;
  2617. *dx = window_dx;
  2618. *dy = window_dy;
  2619. return true;
  2620. }
  2621. bool SDL_RenderCoordinatesFromWindow(SDL_Renderer *renderer, float window_x, float window_y, float *x, float *y)
  2622. {
  2623. float render_x, render_y;
  2624. CHECK_RENDERER_MAGIC(renderer, false);
  2625. // Convert from window coordinates to pixels within the window
  2626. render_x = window_x * renderer->dpi_scale.x;
  2627. render_y = window_y * renderer->dpi_scale.y;
  2628. // Convert from pixels within the window to pixels within the view
  2629. const SDL_RenderViewState *view = &renderer->main_view;
  2630. if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) {
  2631. const SDL_FRect *src = &view->logical_src_rect;
  2632. const SDL_FRect *dst = &view->logical_dst_rect;
  2633. render_x = ((render_x - dst->x) * src->w) / dst->w;
  2634. render_y = ((render_y - dst->y) * src->h) / dst->h;
  2635. }
  2636. render_x = (render_x / view->scale.x) - view->viewport.x;
  2637. render_y = (render_y / view->scale.y) - view->viewport.y;
  2638. if (x) {
  2639. *x = render_x;
  2640. }
  2641. if (y) {
  2642. *y = render_y;
  2643. }
  2644. return true;
  2645. }
  2646. bool SDL_RenderCoordinatesToWindow(SDL_Renderer *renderer, float x, float y, float *window_x, float *window_y)
  2647. {
  2648. CHECK_RENDERER_MAGIC(renderer, false);
  2649. const SDL_RenderViewState *view = &renderer->main_view;
  2650. x = (view->viewport.x + x) * view->scale.x;
  2651. y = (view->viewport.y + y) * view->scale.y;
  2652. // Convert from render coordinates to pixels within the window
  2653. if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) {
  2654. const SDL_FRect *src = &view->logical_src_rect;
  2655. const SDL_FRect *dst = &view->logical_dst_rect;
  2656. x = dst->x + ((x * dst->w) / src->w);
  2657. y = dst->y + ((y * dst->h) / src->h);
  2658. }
  2659. // Convert from pixels within the window to window coordinates
  2660. x /= renderer->dpi_scale.x;
  2661. y /= renderer->dpi_scale.y;
  2662. if (window_x) {
  2663. *window_x = x;
  2664. }
  2665. if (window_y) {
  2666. *window_y = y;
  2667. }
  2668. return true;
  2669. }
  2670. bool SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *event)
  2671. {
  2672. CHECK_RENDERER_MAGIC(renderer, false);
  2673. if (event->type == SDL_EVENT_MOUSE_MOTION) {
  2674. SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
  2675. if (window == renderer->window) {
  2676. SDL_RenderCoordinatesFromWindow(renderer, event->motion.x, event->motion.y, &event->motion.x, &event->motion.y);
  2677. SDL_RenderVectorFromWindow(renderer, event->motion.xrel, event->motion.yrel, &event->motion.xrel, &event->motion.yrel);
  2678. }
  2679. } else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
  2680. event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
  2681. SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
  2682. if (window == renderer->window) {
  2683. SDL_RenderCoordinatesFromWindow(renderer, event->button.x, event->button.y, &event->button.x, &event->button.y);
  2684. }
  2685. } else if (event->type == SDL_EVENT_MOUSE_WHEEL) {
  2686. SDL_Window *window = SDL_GetWindowFromID(event->wheel.windowID);
  2687. if (window == renderer->window) {
  2688. SDL_RenderCoordinatesFromWindow(renderer, event->wheel.mouse_x,
  2689. event->wheel.mouse_y,
  2690. &event->wheel.mouse_x,
  2691. &event->wheel.mouse_y);
  2692. }
  2693. } else if (event->type == SDL_EVENT_FINGER_DOWN ||
  2694. event->type == SDL_EVENT_FINGER_UP ||
  2695. event->type == SDL_EVENT_FINGER_CANCELED ||
  2696. event->type == SDL_EVENT_FINGER_MOTION) {
  2697. // FIXME: Are these events guaranteed to be window relative?
  2698. if (renderer->window) {
  2699. int w, h;
  2700. if (!SDL_GetWindowSize(renderer->window, &w, &h)) {
  2701. return false;
  2702. }
  2703. SDL_RenderCoordinatesFromWindow(renderer, event->tfinger.x * w, event->tfinger.y * h, &event->tfinger.x, &event->tfinger.y);
  2704. SDL_RenderVectorFromWindow(renderer, event->tfinger.dx * w, event->tfinger.dy * h, &event->tfinger.dx, &event->tfinger.dy);
  2705. }
  2706. } else if (event->type == SDL_EVENT_PEN_MOTION) {
  2707. SDL_Window *window = SDL_GetWindowFromID(event->pmotion.windowID);
  2708. if (window == renderer->window) {
  2709. SDL_RenderCoordinatesFromWindow(renderer, event->pmotion.x, event->pmotion.y, &event->pmotion.x, &event->pmotion.y);
  2710. }
  2711. } else if ((event->type == SDL_EVENT_PEN_DOWN) || (event->type == SDL_EVENT_PEN_UP)) {
  2712. SDL_Window *window = SDL_GetWindowFromID(event->ptouch.windowID);
  2713. if (window == renderer->window) {
  2714. SDL_RenderCoordinatesFromWindow(renderer, event->ptouch.x, event->ptouch.y, &event->ptouch.x, &event->ptouch.y);
  2715. }
  2716. } else if ((event->type == SDL_EVENT_PEN_BUTTON_DOWN) || (event->type == SDL_EVENT_PEN_BUTTON_UP)) {
  2717. SDL_Window *window = SDL_GetWindowFromID(event->pbutton.windowID);
  2718. if (window == renderer->window) {
  2719. SDL_RenderCoordinatesFromWindow(renderer, event->pbutton.x, event->pbutton.y, &event->pbutton.x, &event->pbutton.y);
  2720. }
  2721. } else if (event->type == SDL_EVENT_PEN_AXIS) {
  2722. SDL_Window *window = SDL_GetWindowFromID(event->paxis.windowID);
  2723. if (window == renderer->window) {
  2724. SDL_RenderCoordinatesFromWindow(renderer, event->paxis.x, event->paxis.y, &event->paxis.x, &event->paxis.y);
  2725. }
  2726. } else if (event->type == SDL_EVENT_DROP_POSITION ||
  2727. event->type == SDL_EVENT_DROP_FILE ||
  2728. event->type == SDL_EVENT_DROP_TEXT ||
  2729. event->type == SDL_EVENT_DROP_COMPLETE) {
  2730. SDL_Window *window = SDL_GetWindowFromID(event->drop.windowID);
  2731. if (window == renderer->window) {
  2732. SDL_RenderCoordinatesFromWindow(renderer, event->drop.x, event->drop.y, &event->drop.x, &event->drop.y);
  2733. }
  2734. }
  2735. return true;
  2736. }
  2737. bool SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect)
  2738. {
  2739. if (rect) {
  2740. SDL_FRect frect;
  2741. SDL_RectToFRect(rect, &frect);
  2742. return SDL_SetRenderViewportFloat(renderer, &frect);
  2743. } else {
  2744. return SDL_SetRenderViewportFloat(renderer, NULL);
  2745. }
  2746. }
  2747. bool SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect)
  2748. {
  2749. if (rect) {
  2750. SDL_zerop(rect);
  2751. }
  2752. SDL_FRect frect;
  2753. if (SDL_GetRenderViewportFloat(renderer, &frect)) {
  2754. if (rect) {
  2755. rect->x = (int)SDL_floorf(frect.x);
  2756. rect->y = (int)SDL_floorf(frect.y);
  2757. rect->w = (int)SDL_ceilf(frect.w);
  2758. rect->h = (int)SDL_ceilf(frect.h);
  2759. }
  2760. return true;
  2761. } else {
  2762. return false;
  2763. }
  2764. }
  2765. bool SDL_SetRenderViewportFloat(SDL_Renderer *renderer, const SDL_FRect *rect)
  2766. {
  2767. CHECK_RENDERER_MAGIC(renderer, false);
  2768. SDL_RenderViewState *view = renderer->view;
  2769. if (rect) {
  2770. if ((rect->w < 0.0f) || (rect->h < 0.0f)) {
  2771. return SDL_SetError("rect has a negative size");
  2772. }
  2773. SDL_copyp(&view->viewport, rect);
  2774. } else {
  2775. view->viewport.x = view->viewport.y = 0.0f;
  2776. view->viewport.w = view->viewport.h = -1.0f;
  2777. }
  2778. UpdatePixelViewport(renderer, view);
  2779. return QueueCmdSetViewport(renderer);
  2780. }
  2781. bool SDL_GetRenderViewportFloat(SDL_Renderer *renderer, SDL_FRect *rect)
  2782. {
  2783. if (rect) {
  2784. SDL_zerop(rect);
  2785. }
  2786. CHECK_RENDERER_MAGIC(renderer, false);
  2787. if (rect) {
  2788. const SDL_RenderViewState *view = renderer->view;
  2789. rect->x = view->viewport.x;
  2790. rect->y = view->viewport.y;
  2791. if (view->viewport.w >= 0.0f) {
  2792. rect->w = view->viewport.w;
  2793. } else {
  2794. rect->w = view->pixel_w / view->current_scale.x;
  2795. }
  2796. if (view->viewport.h >= 0.0f) {
  2797. rect->h = view->viewport.h;
  2798. } else {
  2799. rect->h = view->pixel_h / view->current_scale.y;
  2800. }
  2801. }
  2802. return true;
  2803. }
  2804. bool SDL_RenderViewportSet(SDL_Renderer *renderer)
  2805. {
  2806. CHECK_RENDERER_MAGIC(renderer, false);
  2807. const SDL_RenderViewState *view = renderer->view;
  2808. return (view->viewport.w >= 0.0f && view->viewport.h >= 0.0f);
  2809. }
  2810. static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect)
  2811. {
  2812. const SDL_RenderViewState *view = renderer->view;
  2813. const float scale_x = view->current_scale.x;
  2814. const float scale_y = view->current_scale.y;
  2815. rect->x = 0.0f;
  2816. rect->y = 0.0f;
  2817. if (view->viewport.w >= 0.0f) {
  2818. rect->w = view->viewport.w;
  2819. } else {
  2820. rect->w = view->pixel_w / scale_x;
  2821. }
  2822. if (view->viewport.h >= 0.0f) {
  2823. rect->h = view->viewport.h;
  2824. } else {
  2825. rect->h = view->pixel_h / scale_y;
  2826. }
  2827. }
  2828. bool SDL_GetRenderSafeArea(SDL_Renderer *renderer, SDL_Rect *rect)
  2829. {
  2830. if (rect) {
  2831. SDL_zerop(rect);
  2832. }
  2833. CHECK_RENDERER_MAGIC(renderer, false);
  2834. if (renderer->target || !renderer->window) {
  2835. // The entire viewport is safe for rendering
  2836. return SDL_GetRenderViewport(renderer, rect);
  2837. }
  2838. if (rect) {
  2839. // Get the window safe rect
  2840. SDL_Rect safe;
  2841. if (!SDL_GetWindowSafeArea(renderer->window, &safe)) {
  2842. return false;
  2843. }
  2844. // Convert the coordinates into the render space
  2845. float minx = (float)safe.x;
  2846. float miny = (float)safe.y;
  2847. float maxx = (float)safe.x + safe.w;
  2848. float maxy = (float)safe.y + safe.h;
  2849. if (!SDL_RenderCoordinatesFromWindow(renderer, minx, miny, &minx, &miny) ||
  2850. !SDL_RenderCoordinatesFromWindow(renderer, maxx, maxy, &maxx, &maxy)) {
  2851. return false;
  2852. }
  2853. rect->x = (int)SDL_ceilf(minx);
  2854. rect->y = (int)SDL_ceilf(miny);
  2855. rect->w = (int)SDL_ceilf(maxx - minx);
  2856. rect->h = (int)SDL_ceilf(maxy - miny);
  2857. // Clip with the viewport
  2858. SDL_Rect viewport;
  2859. if (!SDL_GetRenderViewport(renderer, &viewport)) {
  2860. return false;
  2861. }
  2862. if (!SDL_GetRectIntersection(rect, &viewport, rect)) {
  2863. return SDL_SetError("No safe area within viewport");
  2864. }
  2865. }
  2866. return true;
  2867. }
  2868. bool SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)
  2869. {
  2870. if (rect) {
  2871. SDL_FRect frect;
  2872. SDL_RectToFRect(rect, &frect);
  2873. return SDL_SetRenderClipRectFloat(renderer, &frect);
  2874. } else {
  2875. return SDL_SetRenderClipRectFloat(renderer, NULL);
  2876. }
  2877. }
  2878. bool SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect)
  2879. {
  2880. if (rect) {
  2881. SDL_zerop(rect);
  2882. }
  2883. SDL_FRect frect;
  2884. if (SDL_GetRenderClipRectFloat(renderer, &frect)) {
  2885. rect->x = (int)SDL_floorf(frect.x);
  2886. rect->y = (int)SDL_floorf(frect.y);
  2887. rect->w = (int)SDL_ceilf(frect.w);
  2888. rect->h = (int)SDL_ceilf(frect.h);
  2889. return true;
  2890. } else {
  2891. return false;
  2892. }
  2893. }
  2894. bool SDL_SetRenderClipRectFloat(SDL_Renderer *renderer, const SDL_FRect *rect)
  2895. {
  2896. CHECK_RENDERER_MAGIC(renderer, false);
  2897. SDL_RenderViewState *view = renderer->view;
  2898. if (rect && rect->w >= 0.0f && rect->h >= 0.0f) {
  2899. view->clipping_enabled = true;
  2900. SDL_copyp(&view->clip_rect, rect);
  2901. } else {
  2902. view->clipping_enabled = false;
  2903. SDL_zero(view->clip_rect);
  2904. }
  2905. UpdatePixelClipRect(renderer, view);
  2906. return QueueCmdSetClipRect(renderer);
  2907. }
  2908. bool SDL_GetRenderClipRectFloat(SDL_Renderer *renderer, SDL_FRect *rect)
  2909. {
  2910. if (rect) {
  2911. SDL_zerop(rect);
  2912. }
  2913. CHECK_RENDERER_MAGIC(renderer, false);
  2914. if (rect) {
  2915. SDL_copyp(rect, &renderer->view->clip_rect);
  2916. }
  2917. return true;
  2918. }
  2919. bool SDL_RenderClipEnabled(SDL_Renderer *renderer)
  2920. {
  2921. CHECK_RENDERER_MAGIC(renderer, false);
  2922. return renderer->view->clipping_enabled;
  2923. }
  2924. bool SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY)
  2925. {
  2926. bool result = true;
  2927. CHECK_RENDERER_MAGIC(renderer, false);
  2928. SDL_RenderViewState *view = renderer->view;
  2929. if ((view->scale.x == scaleX) && (view->scale.y == scaleY)) {
  2930. return true;
  2931. }
  2932. view->scale.x = scaleX;
  2933. view->scale.y = scaleY;
  2934. view->current_scale.x = scaleX * view->logical_scale.x;
  2935. view->current_scale.y = scaleY * view->logical_scale.y;
  2936. UpdatePixelViewport(renderer, view);
  2937. UpdatePixelClipRect(renderer, view);
  2938. // The scale affects the existing viewport and clip rectangle
  2939. result &= QueueCmdSetViewport(renderer);
  2940. result &= QueueCmdSetClipRect(renderer);
  2941. return result;
  2942. }
  2943. bool SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY)
  2944. {
  2945. if (scaleX) {
  2946. *scaleX = 1.0f;
  2947. }
  2948. if (scaleY) {
  2949. *scaleY = 1.0f;
  2950. }
  2951. CHECK_RENDERER_MAGIC(renderer, false);
  2952. const SDL_RenderViewState *view = renderer->view;
  2953. if (scaleX) {
  2954. *scaleX = view->scale.x;
  2955. }
  2956. if (scaleY) {
  2957. *scaleY = view->scale.y;
  2958. }
  2959. return true;
  2960. }
  2961. bool SDL_SetRenderDrawColor(SDL_Renderer *renderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
  2962. {
  2963. const float fR = (float)r / 255.0f;
  2964. const float fG = (float)g / 255.0f;
  2965. const float fB = (float)b / 255.0f;
  2966. const float fA = (float)a / 255.0f;
  2967. return SDL_SetRenderDrawColorFloat(renderer, fR, fG, fB, fA);
  2968. }
  2969. bool SDL_SetRenderDrawColorFloat(SDL_Renderer *renderer, float r, float g, float b, float a)
  2970. {
  2971. CHECK_RENDERER_MAGIC(renderer, false);
  2972. renderer->color.r = r;
  2973. renderer->color.g = g;
  2974. renderer->color.b = b;
  2975. renderer->color.a = a;
  2976. return true;
  2977. }
  2978. bool SDL_GetRenderDrawColor(SDL_Renderer *renderer, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)
  2979. {
  2980. float fR, fG, fB, fA;
  2981. if (!SDL_GetRenderDrawColorFloat(renderer, &fR, &fG, &fB, &fA)) {
  2982. if (r) {
  2983. *r = 0;
  2984. }
  2985. if (g) {
  2986. *g = 0;
  2987. }
  2988. if (b) {
  2989. *b = 0;
  2990. }
  2991. if (a) {
  2992. *a = 0;
  2993. }
  2994. return false;
  2995. }
  2996. if (r) {
  2997. *r = (Uint8)(fR * 255.0f);
  2998. }
  2999. if (g) {
  3000. *g = (Uint8)(fG * 255.0f);
  3001. }
  3002. if (b) {
  3003. *b = (Uint8)(fB * 255.0f);
  3004. }
  3005. if (a) {
  3006. *a = (Uint8)(fA * 255.0f);
  3007. }
  3008. return true;
  3009. }
  3010. bool SDL_GetRenderDrawColorFloat(SDL_Renderer *renderer, float *r, float *g, float *b, float *a)
  3011. {
  3012. SDL_FColor color;
  3013. if (r) {
  3014. *r = 0.0f;
  3015. }
  3016. if (g) {
  3017. *g = 0.0f;
  3018. }
  3019. if (b) {
  3020. *b = 0.0f;
  3021. }
  3022. if (a) {
  3023. *a = 0.0f;
  3024. }
  3025. CHECK_RENDERER_MAGIC(renderer, false);
  3026. color = renderer->color;
  3027. if (r) {
  3028. *r = color.r;
  3029. }
  3030. if (g) {
  3031. *g = color.g;
  3032. }
  3033. if (b) {
  3034. *b = color.b;
  3035. }
  3036. if (a) {
  3037. *a = color.a;
  3038. }
  3039. return true;
  3040. }
  3041. bool SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale)
  3042. {
  3043. CHECK_RENDERER_MAGIC(renderer, false);
  3044. renderer->desired_color_scale = scale;
  3045. UpdateColorScale(renderer);
  3046. return true;
  3047. }
  3048. bool SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale)
  3049. {
  3050. if (scale) {
  3051. *scale = 1.0f;
  3052. }
  3053. CHECK_RENDERER_MAGIC(renderer, false);
  3054. if (scale) {
  3055. *scale = renderer->desired_color_scale;
  3056. }
  3057. return true;
  3058. }
  3059. bool SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
  3060. {
  3061. CHECK_RENDERER_MAGIC(renderer, false);
  3062. CHECK_PARAM(blendMode == SDL_BLENDMODE_INVALID) {
  3063. return SDL_InvalidParamError("blendMode");
  3064. }
  3065. if (!IsSupportedBlendMode(renderer, blendMode)) {
  3066. return SDL_Unsupported();
  3067. }
  3068. renderer->blendMode = blendMode;
  3069. return true;
  3070. }
  3071. bool SDL_GetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode *blendMode)
  3072. {
  3073. if (blendMode) {
  3074. *blendMode = SDL_BLENDMODE_INVALID;
  3075. }
  3076. CHECK_RENDERER_MAGIC(renderer, false);
  3077. if (blendMode) {
  3078. *blendMode = renderer->blendMode;
  3079. }
  3080. return true;
  3081. }
  3082. bool SDL_RenderClear(SDL_Renderer *renderer)
  3083. {
  3084. CHECK_RENDERER_MAGIC(renderer, false);
  3085. return QueueCmdClear(renderer);
  3086. }
  3087. bool SDL_RenderPoint(SDL_Renderer *renderer, float x, float y)
  3088. {
  3089. SDL_FPoint fpoint;
  3090. fpoint.x = x;
  3091. fpoint.y = y;
  3092. return SDL_RenderPoints(renderer, &fpoint, 1);
  3093. }
  3094. static bool RenderPointsWithRects(SDL_Renderer *renderer, const SDL_FPoint *fpoints, const int count)
  3095. {
  3096. bool result;
  3097. bool isstack;
  3098. SDL_FRect *frects;
  3099. int i;
  3100. if (count < 1) {
  3101. return true;
  3102. }
  3103. frects = SDL_small_alloc(SDL_FRect, count, &isstack);
  3104. if (!frects) {
  3105. return false;
  3106. }
  3107. const SDL_RenderViewState *view = renderer->view;
  3108. const float scale_x = view->current_scale.x;
  3109. const float scale_y = view->current_scale.y;
  3110. for (i = 0; i < count; ++i) {
  3111. frects[i].x = fpoints[i].x * scale_x;
  3112. frects[i].y = fpoints[i].y * scale_y;
  3113. frects[i].w = scale_x;
  3114. frects[i].h = scale_y;
  3115. }
  3116. result = QueueCmdFillRects(renderer, frects, count);
  3117. SDL_small_free(frects, isstack);
  3118. return result;
  3119. }
  3120. bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count)
  3121. {
  3122. bool result;
  3123. CHECK_RENDERER_MAGIC(renderer, false);
  3124. CHECK_PARAM(!points) {
  3125. return SDL_InvalidParamError("SDL_RenderPoints(): points");
  3126. }
  3127. if (count < 1) {
  3128. return true;
  3129. }
  3130. const SDL_RenderViewState *view = renderer->view;
  3131. if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) {
  3132. result = RenderPointsWithRects(renderer, points, count);
  3133. } else {
  3134. result = QueueCmdDrawPoints(renderer, points, count);
  3135. }
  3136. return result;
  3137. }
  3138. bool SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float y2)
  3139. {
  3140. SDL_FPoint points[2];
  3141. points[0].x = x1;
  3142. points[0].y = y1;
  3143. points[1].x = x2;
  3144. points[1].y = y2;
  3145. return SDL_RenderLines(renderer, points, 2);
  3146. }
  3147. static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2, int y2, bool draw_last)
  3148. {
  3149. const SDL_RenderViewState *view = renderer->view;
  3150. const int MAX_PIXELS = SDL_max(view->pixel_w, view->pixel_h) * 4;
  3151. int i, deltax, deltay, numpixels;
  3152. int d, dinc1, dinc2;
  3153. int x, xinc1, xinc2;
  3154. int y, yinc1, yinc2;
  3155. bool result;
  3156. bool isstack;
  3157. SDL_FPoint *points;
  3158. SDL_Rect viewport;
  3159. /* the backend might clip this further to the clipping rect, but we
  3160. just want a basic safety against generating millions of points for
  3161. massive lines. */
  3162. viewport = view->pixel_viewport;
  3163. viewport.x = 0;
  3164. viewport.y = 0;
  3165. if (!SDL_GetRectAndLineIntersection(&viewport, &x1, &y1, &x2, &y2)) {
  3166. return true;
  3167. }
  3168. deltax = SDL_abs(x2 - x1);
  3169. deltay = SDL_abs(y2 - y1);
  3170. if (deltax >= deltay) {
  3171. numpixels = deltax + 1;
  3172. d = (2 * deltay) - deltax;
  3173. dinc1 = deltay * 2;
  3174. dinc2 = (deltay - deltax) * 2;
  3175. xinc1 = 1;
  3176. xinc2 = 1;
  3177. yinc1 = 0;
  3178. yinc2 = 1;
  3179. } else {
  3180. numpixels = deltay + 1;
  3181. d = (2 * deltax) - deltay;
  3182. dinc1 = deltax * 2;
  3183. dinc2 = (deltax - deltay) * 2;
  3184. xinc1 = 0;
  3185. xinc2 = 1;
  3186. yinc1 = 1;
  3187. yinc2 = 1;
  3188. }
  3189. if (x1 > x2) {
  3190. xinc1 = -xinc1;
  3191. xinc2 = -xinc2;
  3192. }
  3193. if (y1 > y2) {
  3194. yinc1 = -yinc1;
  3195. yinc2 = -yinc2;
  3196. }
  3197. x = x1;
  3198. y = y1;
  3199. if (!draw_last) {
  3200. --numpixels;
  3201. }
  3202. if (numpixels > MAX_PIXELS) {
  3203. return SDL_SetError("Line too long (tried to draw %d pixels, max %d)", numpixels, MAX_PIXELS);
  3204. }
  3205. points = SDL_small_alloc(SDL_FPoint, numpixels, &isstack);
  3206. if (!points) {
  3207. return false;
  3208. }
  3209. for (i = 0; i < numpixels; ++i) {
  3210. points[i].x = (float)x;
  3211. points[i].y = (float)y;
  3212. if (d < 0) {
  3213. d += dinc1;
  3214. x += xinc1;
  3215. y += yinc1;
  3216. } else {
  3217. d += dinc2;
  3218. x += xinc2;
  3219. y += yinc2;
  3220. }
  3221. }
  3222. if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) {
  3223. result = RenderPointsWithRects(renderer, points, numpixels);
  3224. } else {
  3225. result = QueueCmdDrawPoints(renderer, points, numpixels);
  3226. }
  3227. SDL_small_free(points, isstack);
  3228. return result;
  3229. }
  3230. static bool RenderLinesWithRectsF(SDL_Renderer *renderer, const SDL_FPoint *points, const int count)
  3231. {
  3232. const SDL_RenderViewState *view = renderer->view;
  3233. const float scale_x = view->current_scale.x;
  3234. const float scale_y = view->current_scale.y;
  3235. SDL_FRect *frect;
  3236. SDL_FRect *frects;
  3237. int i, nrects = 0;
  3238. bool result = true;
  3239. bool isstack;
  3240. bool drew_line = false;
  3241. bool draw_last = false;
  3242. frects = SDL_small_alloc(SDL_FRect, count - 1, &isstack);
  3243. if (!frects) {
  3244. return false;
  3245. }
  3246. for (i = 0; i < count - 1; ++i) {
  3247. bool same_x = (points[i].x == points[i + 1].x);
  3248. bool same_y = (points[i].y == points[i + 1].y);
  3249. if (i == (count - 2)) {
  3250. if (!drew_line || points[i + 1].x != points[0].x || points[i + 1].y != points[0].y) {
  3251. draw_last = true;
  3252. }
  3253. } else {
  3254. if (same_x && same_y) {
  3255. continue;
  3256. }
  3257. }
  3258. if (same_x) {
  3259. const float minY = SDL_min(points[i].y, points[i + 1].y);
  3260. const float maxY = SDL_max(points[i].y, points[i + 1].y);
  3261. frect = &frects[nrects++];
  3262. frect->x = points[i].x * scale_x;
  3263. frect->y = minY * scale_y;
  3264. frect->w = scale_x;
  3265. frect->h = (maxY - minY + draw_last) * scale_y;
  3266. if (!draw_last && points[i + 1].y < points[i].y) {
  3267. frect->y += scale_y;
  3268. }
  3269. } else if (same_y) {
  3270. const float minX = SDL_min(points[i].x, points[i + 1].x);
  3271. const float maxX = SDL_max(points[i].x, points[i + 1].x);
  3272. frect = &frects[nrects++];
  3273. frect->x = minX * scale_x;
  3274. frect->y = points[i].y * scale_y;
  3275. frect->w = (maxX - minX + draw_last) * scale_x;
  3276. frect->h = scale_y;
  3277. if (!draw_last && points[i + 1].x < points[i].x) {
  3278. frect->x += scale_x;
  3279. }
  3280. } else {
  3281. result &= RenderLineBresenham(renderer, (int)SDL_roundf(points[i].x), (int)SDL_roundf(points[i].y),
  3282. (int)SDL_roundf(points[i + 1].x), (int)SDL_roundf(points[i + 1].y), draw_last);
  3283. }
  3284. drew_line = true;
  3285. }
  3286. if (nrects) {
  3287. result &= QueueCmdFillRects(renderer, frects, nrects);
  3288. }
  3289. SDL_small_free(frects, isstack);
  3290. return result;
  3291. }
  3292. bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count)
  3293. {
  3294. bool result = true;
  3295. CHECK_RENDERER_MAGIC(renderer, false);
  3296. CHECK_PARAM(!points) {
  3297. return SDL_InvalidParamError("SDL_RenderLines(): points");
  3298. }
  3299. if (count < 2) {
  3300. return true;
  3301. }
  3302. SDL_RenderViewState *view = renderer->view;
  3303. const bool islogical = (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED);
  3304. if (islogical || (renderer->line_method == SDL_RENDERLINEMETHOD_GEOMETRY)) {
  3305. const float scale_x = view->current_scale.x;
  3306. const float scale_y = view->current_scale.y;
  3307. bool isstack1;
  3308. bool isstack2;
  3309. float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1);
  3310. int *indices = SDL_small_alloc(int, (4) * 3 * (count - 1) + (2) * 3 * (count), &isstack2);
  3311. if (xy && indices) {
  3312. int i;
  3313. float *ptr_xy = xy;
  3314. int *ptr_indices = indices;
  3315. const int xy_stride = 2 * sizeof(float);
  3316. int num_vertices = 4 * count;
  3317. int num_indices = 0;
  3318. const int size_indices = 4;
  3319. int cur_index = -4;
  3320. const bool is_looping = (points[0].x == points[count - 1].x && points[0].y == points[count - 1].y);
  3321. SDL_FPoint p; // previous point
  3322. p.x = p.y = 0.0f;
  3323. /* p q
  3324. 0----1------ 4----5
  3325. | \ |``\ | \ |
  3326. | \ | ` `\| \ |
  3327. 3----2-------7----6
  3328. */
  3329. for (i = 0; i < count; ++i) {
  3330. SDL_FPoint q = points[i]; // current point
  3331. q.x *= scale_x;
  3332. q.y *= scale_y;
  3333. *ptr_xy++ = q.x;
  3334. *ptr_xy++ = q.y;
  3335. *ptr_xy++ = q.x + scale_x;
  3336. *ptr_xy++ = q.y;
  3337. *ptr_xy++ = q.x + scale_x;
  3338. *ptr_xy++ = q.y + scale_y;
  3339. *ptr_xy++ = q.x;
  3340. *ptr_xy++ = q.y + scale_y;
  3341. #define ADD_TRIANGLE(i1, i2, i3) \
  3342. *ptr_indices++ = cur_index + (i1); \
  3343. *ptr_indices++ = cur_index + (i2); \
  3344. *ptr_indices++ = cur_index + (i3); \
  3345. num_indices += 3;
  3346. // closed polyline, don´t draw twice the point
  3347. if (i || !is_looping) {
  3348. ADD_TRIANGLE(4, 5, 6)
  3349. ADD_TRIANGLE(4, 6, 7)
  3350. }
  3351. // first point only, no segment
  3352. if (i == 0) {
  3353. p = q;
  3354. cur_index += 4;
  3355. continue;
  3356. }
  3357. // draw segment
  3358. if (p.y == q.y) {
  3359. if (p.x < q.x) {
  3360. ADD_TRIANGLE(1, 4, 7)
  3361. ADD_TRIANGLE(1, 7, 2)
  3362. } else {
  3363. ADD_TRIANGLE(5, 0, 3)
  3364. ADD_TRIANGLE(5, 3, 6)
  3365. }
  3366. } else if (p.x == q.x) {
  3367. if (p.y < q.y) {
  3368. ADD_TRIANGLE(2, 5, 4)
  3369. ADD_TRIANGLE(2, 4, 3)
  3370. } else {
  3371. ADD_TRIANGLE(6, 1, 0)
  3372. ADD_TRIANGLE(6, 0, 7)
  3373. }
  3374. } else {
  3375. if (p.y < q.y) {
  3376. if (p.x < q.x) {
  3377. ADD_TRIANGLE(1, 5, 4)
  3378. ADD_TRIANGLE(1, 4, 2)
  3379. ADD_TRIANGLE(2, 4, 7)
  3380. ADD_TRIANGLE(2, 7, 3)
  3381. } else {
  3382. ADD_TRIANGLE(4, 0, 5)
  3383. ADD_TRIANGLE(5, 0, 3)
  3384. ADD_TRIANGLE(5, 3, 6)
  3385. ADD_TRIANGLE(6, 3, 2)
  3386. }
  3387. } else {
  3388. if (p.x < q.x) {
  3389. ADD_TRIANGLE(0, 4, 7)
  3390. ADD_TRIANGLE(0, 7, 1)
  3391. ADD_TRIANGLE(1, 7, 6)
  3392. ADD_TRIANGLE(1, 6, 2)
  3393. } else {
  3394. ADD_TRIANGLE(6, 5, 1)
  3395. ADD_TRIANGLE(6, 1, 0)
  3396. ADD_TRIANGLE(7, 6, 0)
  3397. ADD_TRIANGLE(7, 0, 3)
  3398. }
  3399. }
  3400. }
  3401. #undef ADD_TRIANGLE
  3402. p = q;
  3403. cur_index += 4;
  3404. }
  3405. result = QueueCmdGeometry(renderer, NULL,
  3406. xy, xy_stride, &renderer->color, 0 /* color_stride */, NULL, 0,
  3407. num_vertices, indices, num_indices, size_indices,
  3408. 1.0f, 1.0f, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
  3409. }
  3410. SDL_small_free(xy, isstack1);
  3411. SDL_small_free(indices, isstack2);
  3412. } else if (renderer->line_method == SDL_RENDERLINEMETHOD_POINTS) {
  3413. result = RenderLinesWithRectsF(renderer, points, count);
  3414. } else if (view->scale.x != 1.0f || view->scale.y != 1.0f) { /* we checked for logical scale elsewhere. */
  3415. result = RenderLinesWithRectsF(renderer, points, count);
  3416. } else {
  3417. result = QueueCmdDrawLines(renderer, points, count);
  3418. }
  3419. return result;
  3420. }
  3421. bool SDL_RenderRect(SDL_Renderer *renderer, const SDL_FRect *rect)
  3422. {
  3423. SDL_FRect frect;
  3424. SDL_FPoint points[5];
  3425. CHECK_RENDERER_MAGIC(renderer, false);
  3426. // If 'rect' == NULL, then outline the whole surface
  3427. if (!rect) {
  3428. GetRenderViewportSize(renderer, &frect);
  3429. rect = &frect;
  3430. }
  3431. points[0].x = rect->x;
  3432. points[0].y = rect->y;
  3433. points[1].x = rect->x + rect->w - 1;
  3434. points[1].y = rect->y;
  3435. points[2].x = rect->x + rect->w - 1;
  3436. points[2].y = rect->y + rect->h - 1;
  3437. points[3].x = rect->x;
  3438. points[3].y = rect->y + rect->h - 1;
  3439. points[4].x = rect->x;
  3440. points[4].y = rect->y;
  3441. return SDL_RenderLines(renderer, points, 5);
  3442. }
  3443. bool SDL_RenderRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count)
  3444. {
  3445. int i;
  3446. CHECK_RENDERER_MAGIC(renderer, false);
  3447. CHECK_PARAM(!rects) {
  3448. return SDL_InvalidParamError("SDL_RenderRects(): rects");
  3449. }
  3450. if (count < 1) {
  3451. return true;
  3452. }
  3453. for (i = 0; i < count; ++i) {
  3454. if (!SDL_RenderRect(renderer, &rects[i])) {
  3455. return false;
  3456. }
  3457. }
  3458. return true;
  3459. }
  3460. bool SDL_RenderFillRect(SDL_Renderer *renderer, const SDL_FRect *rect)
  3461. {
  3462. SDL_FRect frect;
  3463. CHECK_RENDERER_MAGIC(renderer, false);
  3464. // If 'rect' == NULL, then fill the whole surface
  3465. if (!rect) {
  3466. GetRenderViewportSize(renderer, &frect);
  3467. rect = &frect;
  3468. }
  3469. return SDL_RenderFillRects(renderer, rect, 1);
  3470. }
  3471. bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count)
  3472. {
  3473. SDL_FRect *frects;
  3474. int i;
  3475. bool result;
  3476. bool isstack;
  3477. CHECK_RENDERER_MAGIC(renderer, false);
  3478. CHECK_PARAM(!rects) {
  3479. return SDL_InvalidParamError("SDL_RenderFillRects(): rects");
  3480. }
  3481. if (count < 1) {
  3482. return true;
  3483. }
  3484. frects = SDL_small_alloc(SDL_FRect, count, &isstack);
  3485. if (!frects) {
  3486. return false;
  3487. }
  3488. const SDL_RenderViewState *view = renderer->view;
  3489. const float scale_x = view->current_scale.x;
  3490. const float scale_y = view->current_scale.y;
  3491. for (i = 0; i < count; ++i) {
  3492. frects[i].x = rects[i].x * scale_x;
  3493. frects[i].y = rects[i].y * scale_y;
  3494. frects[i].w = rects[i].w * scale_x;
  3495. frects[i].h = rects[i].h * scale_y;
  3496. }
  3497. result = QueueCmdFillRects(renderer, frects, count);
  3498. SDL_small_free(frects, isstack);
  3499. return result;
  3500. }
  3501. static bool SDL_RenderTextureInternal(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect)
  3502. {
  3503. const SDL_RenderViewState *view = renderer->view;
  3504. const float scale_x = view->current_scale.x;
  3505. const float scale_y = view->current_scale.y;
  3506. const bool use_rendergeometry = (!renderer->QueueCopy);
  3507. bool result;
  3508. if (use_rendergeometry) {
  3509. float xy[8];
  3510. const int xy_stride = 2 * sizeof(float);
  3511. float uv[8];
  3512. const int uv_stride = 2 * sizeof(float);
  3513. const int num_vertices = 4;
  3514. const int *indices = rect_index_order;
  3515. const int num_indices = 6;
  3516. const int size_indices = 4;
  3517. float minu, minv, maxu, maxv;
  3518. float minx, miny, maxx, maxy;
  3519. minu = srcrect->x / texture->w;
  3520. minv = srcrect->y / texture->h;
  3521. maxu = (srcrect->x + srcrect->w) / texture->w;
  3522. maxv = (srcrect->y + srcrect->h) / texture->h;
  3523. minx = dstrect->x;
  3524. miny = dstrect->y;
  3525. maxx = dstrect->x + dstrect->w;
  3526. maxy = dstrect->y + dstrect->h;
  3527. uv[0] = minu;
  3528. uv[1] = minv;
  3529. uv[2] = maxu;
  3530. uv[3] = minv;
  3531. uv[4] = maxu;
  3532. uv[5] = maxv;
  3533. uv[6] = minu;
  3534. uv[7] = maxv;
  3535. xy[0] = minx;
  3536. xy[1] = miny;
  3537. xy[2] = maxx;
  3538. xy[3] = miny;
  3539. xy[4] = maxx;
  3540. xy[5] = maxy;
  3541. xy[6] = minx;
  3542. xy[7] = maxy;
  3543. result = QueueCmdGeometry(renderer, texture,
  3544. xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride,
  3545. num_vertices, indices, num_indices, size_indices,
  3546. scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
  3547. } else {
  3548. const SDL_FRect rect = { dstrect->x * scale_x, dstrect->y * scale_y, dstrect->w * scale_x, dstrect->h * scale_y };
  3549. result = QueueCmdCopy(renderer, texture, srcrect, &rect);
  3550. }
  3551. return result;
  3552. }
  3553. bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect)
  3554. {
  3555. CHECK_RENDERER_MAGIC(renderer, false);
  3556. CHECK_TEXTURE_MAGIC(texture, false);
  3557. CHECK_PARAM(renderer != texture->renderer) {
  3558. return SDL_SetError("Texture was not created with this renderer");
  3559. }
  3560. SDL_FRect real_srcrect;
  3561. real_srcrect.x = 0.0f;
  3562. real_srcrect.y = 0.0f;
  3563. real_srcrect.w = (float)texture->w;
  3564. real_srcrect.h = (float)texture->h;
  3565. if (srcrect) {
  3566. if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) {
  3567. return true;
  3568. }
  3569. }
  3570. SDL_FRect full_dstrect;
  3571. if (!dstrect) {
  3572. GetRenderViewportSize(renderer, &full_dstrect);
  3573. dstrect = &full_dstrect;
  3574. }
  3575. if (!UpdateTexturePalette(texture)) {
  3576. return false;
  3577. }
  3578. if (texture->native) {
  3579. texture = texture->native;
  3580. }
  3581. texture->last_command_generation = renderer->render_command_generation;
  3582. return SDL_RenderTextureInternal(renderer, texture, &real_srcrect, dstrect);
  3583. }
  3584. bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture,
  3585. const SDL_FRect *srcrect, const SDL_FPoint *origin, const SDL_FPoint *right, const SDL_FPoint *down)
  3586. {
  3587. SDL_FRect real_srcrect;
  3588. SDL_FRect real_dstrect;
  3589. bool result;
  3590. CHECK_RENDERER_MAGIC(renderer, false);
  3591. CHECK_TEXTURE_MAGIC(texture, false);
  3592. CHECK_PARAM(renderer != texture->renderer) {
  3593. return SDL_SetError("Texture was not created with this renderer");
  3594. }
  3595. if (!renderer->QueueCopyEx && !renderer->QueueGeometry) {
  3596. return SDL_SetError("Renderer does not support RenderCopyEx");
  3597. }
  3598. real_srcrect.x = 0.0f;
  3599. real_srcrect.y = 0.0f;
  3600. real_srcrect.w = (float)texture->w;
  3601. real_srcrect.h = (float)texture->h;
  3602. if (srcrect) {
  3603. if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) {
  3604. return true;
  3605. }
  3606. }
  3607. GetRenderViewportSize(renderer, &real_dstrect);
  3608. if (!UpdateTexturePalette(texture)) {
  3609. return false;
  3610. }
  3611. if (texture->native) {
  3612. texture = texture->native;
  3613. }
  3614. texture->last_command_generation = renderer->render_command_generation;
  3615. const SDL_RenderViewState *view = renderer->view;
  3616. const float scale_x = view->current_scale.x;
  3617. const float scale_y = view->current_scale.y;
  3618. {
  3619. float xy[8];
  3620. const int xy_stride = 2 * sizeof(float);
  3621. float uv[8];
  3622. const int uv_stride = 2 * sizeof(float);
  3623. const int num_vertices = 4;
  3624. const int *indices = rect_index_order;
  3625. const int num_indices = 6;
  3626. const int size_indices = 4;
  3627. float minu = real_srcrect.x / texture->w;
  3628. float minv = real_srcrect.y / texture->h;
  3629. float maxu = (real_srcrect.x + real_srcrect.w) / texture->w;
  3630. float maxv = (real_srcrect.y + real_srcrect.h) / texture->h;
  3631. uv[0] = minu;
  3632. uv[1] = minv;
  3633. uv[2] = maxu;
  3634. uv[3] = minv;
  3635. uv[4] = maxu;
  3636. uv[5] = maxv;
  3637. uv[6] = minu;
  3638. uv[7] = maxv;
  3639. // (minx, miny)
  3640. if (origin) {
  3641. xy[0] = origin->x;
  3642. xy[1] = origin->y;
  3643. } else {
  3644. xy[0] = real_dstrect.x;
  3645. xy[1] = real_dstrect.y;
  3646. }
  3647. // (maxx, miny)
  3648. if (right) {
  3649. xy[2] = right->x;
  3650. xy[3] = right->y;
  3651. } else {
  3652. xy[2] = real_dstrect.x + real_dstrect.w;
  3653. xy[3] = real_dstrect.y;
  3654. }
  3655. // (minx, maxy)
  3656. if (down) {
  3657. xy[6] = down->x;
  3658. xy[7] = down->y;
  3659. } else {
  3660. xy[6] = real_dstrect.x;
  3661. xy[7] = real_dstrect.y + real_dstrect.h;
  3662. }
  3663. // (maxx, maxy)
  3664. if (origin || right || down) {
  3665. xy[4] = xy[2] + xy[6] - xy[0];
  3666. xy[5] = xy[3] + xy[7] - xy[1];
  3667. } else {
  3668. xy[4] = real_dstrect.x + real_dstrect.w;
  3669. xy[5] = real_dstrect.y + real_dstrect.h;
  3670. }
  3671. result = QueueCmdGeometry(
  3672. renderer, texture,
  3673. xy, xy_stride,
  3674. &texture->color, 0 /* color_stride */,
  3675. uv, uv_stride,
  3676. num_vertices, indices, num_indices, size_indices,
  3677. scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP
  3678. );
  3679. }
  3680. return result;
  3681. }
  3682. bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture,
  3683. const SDL_FRect *srcrect, const SDL_FRect *dstrect,
  3684. const double angle, const SDL_FPoint *center, const SDL_FlipMode flip)
  3685. {
  3686. SDL_FRect real_srcrect;
  3687. SDL_FPoint real_center;
  3688. bool result;
  3689. if (flip == SDL_FLIP_NONE && (int)(angle / 360) == angle / 360) { // fast path when we don't need rotation or flipping
  3690. return SDL_RenderTexture(renderer, texture, srcrect, dstrect);
  3691. }
  3692. CHECK_RENDERER_MAGIC(renderer, false);
  3693. CHECK_TEXTURE_MAGIC(texture, false);
  3694. CHECK_PARAM(renderer != texture->renderer) {
  3695. return SDL_SetError("Texture was not created with this renderer");
  3696. }
  3697. if (!renderer->QueueCopyEx && !renderer->QueueGeometry) {
  3698. return SDL_SetError("Renderer does not support RenderCopyEx");
  3699. }
  3700. real_srcrect.x = 0.0f;
  3701. real_srcrect.y = 0.0f;
  3702. real_srcrect.w = (float)texture->w;
  3703. real_srcrect.h = (float)texture->h;
  3704. if (srcrect) {
  3705. if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) {
  3706. return true;
  3707. }
  3708. }
  3709. // We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we?
  3710. SDL_FRect full_dstrect;
  3711. if (!dstrect) {
  3712. GetRenderViewportSize(renderer, &full_dstrect);
  3713. dstrect = &full_dstrect;
  3714. }
  3715. if (!UpdateTexturePalette(texture)) {
  3716. return false;
  3717. }
  3718. if (texture->native) {
  3719. texture = texture->native;
  3720. }
  3721. if (center) {
  3722. real_center = *center;
  3723. } else {
  3724. real_center.x = dstrect->w / 2.0f;
  3725. real_center.y = dstrect->h / 2.0f;
  3726. }
  3727. texture->last_command_generation = renderer->render_command_generation;
  3728. const SDL_RenderViewState *view = renderer->view;
  3729. const float scale_x = view->current_scale.x;
  3730. const float scale_y = view->current_scale.y;
  3731. const bool use_rendergeometry = (!renderer->QueueCopyEx);
  3732. if (use_rendergeometry) {
  3733. float xy[8];
  3734. const int xy_stride = 2 * sizeof(float);
  3735. float uv[8];
  3736. const int uv_stride = 2 * sizeof(float);
  3737. const int num_vertices = 4;
  3738. const int *indices = rect_index_order;
  3739. const int num_indices = 6;
  3740. const int size_indices = 4;
  3741. float minu, minv, maxu, maxv;
  3742. float minx, miny, maxx, maxy;
  3743. float centerx, centery;
  3744. float s_minx, s_miny, s_maxx, s_maxy;
  3745. float c_minx, c_miny, c_maxx, c_maxy;
  3746. const float radian_angle = (float)((SDL_PI_D * angle) / 180.0);
  3747. const float s = SDL_sinf(radian_angle);
  3748. const float c = SDL_cosf(radian_angle);
  3749. minu = real_srcrect.x / texture->w;
  3750. minv = real_srcrect.y / texture->h;
  3751. maxu = (real_srcrect.x + real_srcrect.w) / texture->w;
  3752. maxv = (real_srcrect.y + real_srcrect.h) / texture->h;
  3753. centerx = real_center.x + dstrect->x;
  3754. centery = real_center.y + dstrect->y;
  3755. if (flip & SDL_FLIP_HORIZONTAL) {
  3756. minx = dstrect->x + dstrect->w;
  3757. maxx = dstrect->x;
  3758. } else {
  3759. minx = dstrect->x;
  3760. maxx = dstrect->x + dstrect->w;
  3761. }
  3762. if (flip & SDL_FLIP_VERTICAL) {
  3763. miny = dstrect->y + dstrect->h;
  3764. maxy = dstrect->y;
  3765. } else {
  3766. miny = dstrect->y;
  3767. maxy = dstrect->y + dstrect->h;
  3768. }
  3769. uv[0] = minu;
  3770. uv[1] = minv;
  3771. uv[2] = maxu;
  3772. uv[3] = minv;
  3773. uv[4] = maxu;
  3774. uv[5] = maxv;
  3775. uv[6] = minu;
  3776. uv[7] = maxv;
  3777. /* apply rotation with 2x2 matrix ( c -s )
  3778. * ( s c ) */
  3779. s_minx = s * (minx - centerx);
  3780. s_miny = s * (miny - centery);
  3781. s_maxx = s * (maxx - centerx);
  3782. s_maxy = s * (maxy - centery);
  3783. c_minx = c * (minx - centerx);
  3784. c_miny = c * (miny - centery);
  3785. c_maxx = c * (maxx - centerx);
  3786. c_maxy = c * (maxy - centery);
  3787. // (minx, miny)
  3788. xy[0] = (c_minx - s_miny) + centerx;
  3789. xy[1] = (s_minx + c_miny) + centery;
  3790. // (maxx, miny)
  3791. xy[2] = (c_maxx - s_miny) + centerx;
  3792. xy[3] = (s_maxx + c_miny) + centery;
  3793. // (maxx, maxy)
  3794. xy[4] = (c_maxx - s_maxy) + centerx;
  3795. xy[5] = (s_maxx + c_maxy) + centery;
  3796. // (minx, maxy)
  3797. xy[6] = (c_minx - s_maxy) + centerx;
  3798. xy[7] = (s_minx + c_maxy) + centery;
  3799. result = QueueCmdGeometry(renderer, texture,
  3800. xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride,
  3801. num_vertices, indices, num_indices, size_indices,
  3802. scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
  3803. } else {
  3804. result = QueueCmdCopyEx(renderer, texture, &real_srcrect, dstrect, angle, &real_center, flip, scale_x, scale_y);
  3805. }
  3806. return result;
  3807. }
  3808. static bool SDL_RenderTextureTiled_Wrap(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect)
  3809. {
  3810. float xy[8];
  3811. const int xy_stride = 2 * sizeof(float);
  3812. float uv[8];
  3813. const int uv_stride = 2 * sizeof(float);
  3814. const int num_vertices = 4;
  3815. const int *indices = rect_index_order;
  3816. const int num_indices = 6;
  3817. const int size_indices = 4;
  3818. float minu, minv, maxu, maxv;
  3819. float minx, miny, maxx, maxy;
  3820. minu = 0.0f;
  3821. minv = 0.0f;
  3822. maxu = dstrect->w / (srcrect->w * scale);
  3823. maxv = dstrect->h / (srcrect->h * scale);
  3824. minx = dstrect->x;
  3825. miny = dstrect->y;
  3826. maxx = dstrect->x + dstrect->w;
  3827. maxy = dstrect->y + dstrect->h;
  3828. uv[0] = minu;
  3829. uv[1] = minv;
  3830. uv[2] = maxu;
  3831. uv[3] = minv;
  3832. uv[4] = maxu;
  3833. uv[5] = maxv;
  3834. uv[6] = minu;
  3835. uv[7] = maxv;
  3836. xy[0] = minx;
  3837. xy[1] = miny;
  3838. xy[2] = maxx;
  3839. xy[3] = miny;
  3840. xy[4] = maxx;
  3841. xy[5] = maxy;
  3842. xy[6] = minx;
  3843. xy[7] = maxy;
  3844. const SDL_RenderViewState *view = renderer->view;
  3845. return QueueCmdGeometry(renderer, texture,
  3846. xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride,
  3847. num_vertices, indices, num_indices, size_indices,
  3848. view->current_scale.x, view->current_scale.y,
  3849. SDL_TEXTURE_ADDRESS_WRAP, SDL_TEXTURE_ADDRESS_WRAP);
  3850. }
  3851. static bool SDL_RenderTextureTiled_Iterate(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect)
  3852. {
  3853. float tile_width = srcrect->w * scale;
  3854. float tile_height = srcrect->h * scale;
  3855. float float_rows, float_cols;
  3856. float remaining_w = SDL_modff(dstrect->w / tile_width, &float_cols);
  3857. float remaining_h = SDL_modff(dstrect->h / tile_height, &float_rows);
  3858. float remaining_src_w = remaining_w * srcrect->w;
  3859. float remaining_src_h = remaining_h * srcrect->h;
  3860. float remaining_dst_w = remaining_w * tile_width;
  3861. float remaining_dst_h = remaining_h * tile_height;
  3862. int rows = (int)float_rows;
  3863. int cols = (int)float_cols;
  3864. SDL_FRect curr_src, curr_dst;
  3865. SDL_copyp(&curr_src, srcrect);
  3866. curr_dst.y = dstrect->y;
  3867. curr_dst.w = tile_width;
  3868. curr_dst.h = tile_height;
  3869. for (int y = 0; y < rows; ++y) {
  3870. curr_dst.x = dstrect->x;
  3871. for (int x = 0; x < cols; ++x) {
  3872. if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) {
  3873. return false;
  3874. }
  3875. curr_dst.x += curr_dst.w;
  3876. }
  3877. if (remaining_dst_w > 0.0f) {
  3878. curr_src.w = remaining_src_w;
  3879. curr_dst.w = remaining_dst_w;
  3880. if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) {
  3881. return false;
  3882. }
  3883. curr_src.w = srcrect->w;
  3884. curr_dst.w = tile_width;
  3885. }
  3886. curr_dst.y += curr_dst.h;
  3887. }
  3888. if (remaining_dst_h > 0.0f) {
  3889. curr_src.h = remaining_src_h;
  3890. curr_dst.h = remaining_dst_h;
  3891. curr_dst.x = dstrect->x;
  3892. for (int x = 0; x < cols; ++x) {
  3893. if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) {
  3894. return false;
  3895. }
  3896. curr_dst.x += curr_dst.w;
  3897. }
  3898. if (remaining_dst_w > 0.0f) {
  3899. curr_src.w = remaining_src_w;
  3900. curr_dst.w = remaining_dst_w;
  3901. if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) {
  3902. return false;
  3903. }
  3904. }
  3905. }
  3906. return true;
  3907. }
  3908. static bool IsNPOT(int x)
  3909. {
  3910. return (x <= 0) || ((x & (x - 1)) != 0);
  3911. }
  3912. bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect)
  3913. {
  3914. SDL_FRect real_srcrect;
  3915. CHECK_RENDERER_MAGIC(renderer, false);
  3916. CHECK_TEXTURE_MAGIC(texture, false);
  3917. CHECK_PARAM(renderer != texture->renderer) {
  3918. return SDL_SetError("Texture was not created with this renderer");
  3919. }
  3920. CHECK_PARAM(scale <= 0.0f) {
  3921. return SDL_InvalidParamError("scale");
  3922. }
  3923. real_srcrect.x = 0.0f;
  3924. real_srcrect.y = 0.0f;
  3925. real_srcrect.w = (float)texture->w;
  3926. real_srcrect.h = (float)texture->h;
  3927. if (srcrect) {
  3928. if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) {
  3929. return true;
  3930. }
  3931. }
  3932. SDL_FRect full_dstrect;
  3933. if (!dstrect) {
  3934. GetRenderViewportSize(renderer, &full_dstrect);
  3935. dstrect = &full_dstrect;
  3936. }
  3937. if (!UpdateTexturePalette(texture)) {
  3938. return false;
  3939. }
  3940. if (texture->native) {
  3941. texture = texture->native;
  3942. }
  3943. texture->last_command_generation = renderer->render_command_generation;
  3944. bool do_wrapping = !renderer->software &&
  3945. (!srcrect ||
  3946. (real_srcrect.x == 0.0f && real_srcrect.y == 0.0f &&
  3947. real_srcrect.w == (float)texture->w && real_srcrect.h == (float)texture->h));
  3948. if (do_wrapping && renderer->npot_texture_wrap_unsupported) {
  3949. if (IsNPOT(texture->w) || IsNPOT(texture->h)) {
  3950. do_wrapping = false;
  3951. }
  3952. }
  3953. // See if we can use geometry with repeating texture coordinates
  3954. if (do_wrapping) {
  3955. return SDL_RenderTextureTiled_Wrap(renderer, texture, &real_srcrect, scale, dstrect);
  3956. } else {
  3957. return SDL_RenderTextureTiled_Iterate(renderer, texture, &real_srcrect, scale, dstrect);
  3958. }
  3959. }
  3960. bool SDL_RenderTexture9Grid(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float left_width, float right_width, float top_height, float bottom_height, float scale, const SDL_FRect *dstrect)
  3961. {
  3962. SDL_FRect full_src, full_dst;
  3963. SDL_FRect curr_src, curr_dst;
  3964. float dst_left_width;
  3965. float dst_right_width;
  3966. float dst_top_height;
  3967. float dst_bottom_height;
  3968. CHECK_RENDERER_MAGIC(renderer, false);
  3969. CHECK_TEXTURE_MAGIC(texture, false);
  3970. CHECK_PARAM(renderer != texture->renderer) {
  3971. return SDL_SetError("Texture was not created with this renderer");
  3972. }
  3973. if (!srcrect) {
  3974. full_src.x = 0;
  3975. full_src.y = 0;
  3976. full_src.w = (float)texture->w;
  3977. full_src.h = (float)texture->h;
  3978. srcrect = &full_src;
  3979. }
  3980. if (!dstrect) {
  3981. GetRenderViewportSize(renderer, &full_dst);
  3982. dstrect = &full_dst;
  3983. }
  3984. if (scale <= 0.0f || scale == 1.0f) {
  3985. dst_left_width = SDL_ceilf(left_width);
  3986. dst_right_width = SDL_ceilf(right_width);
  3987. dst_top_height = SDL_ceilf(top_height);
  3988. dst_bottom_height = SDL_ceilf(bottom_height);
  3989. } else {
  3990. dst_left_width = SDL_ceilf(left_width * scale);
  3991. dst_right_width = SDL_ceilf(right_width * scale);
  3992. dst_top_height = SDL_ceilf(top_height * scale);
  3993. dst_bottom_height = SDL_ceilf(bottom_height * scale);
  3994. }
  3995. // Center
  3996. curr_src.x = srcrect->x + left_width;
  3997. curr_src.y = srcrect->y + top_height;
  3998. curr_src.w = srcrect->w - left_width - right_width;
  3999. curr_src.h = srcrect->h - top_height - bottom_height;
  4000. curr_dst.x = dstrect->x + dst_left_width;
  4001. curr_dst.y = dstrect->y + dst_top_height;
  4002. curr_dst.w = dstrect->w - dst_left_width - dst_right_width;
  4003. curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height;
  4004. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4005. return false;
  4006. }
  4007. // Upper-left corner
  4008. curr_src.x = srcrect->x;
  4009. curr_src.y = srcrect->y;
  4010. curr_src.w = left_width;
  4011. curr_src.h = top_height;
  4012. curr_dst.x = dstrect->x;
  4013. curr_dst.y = dstrect->y;
  4014. curr_dst.w = dst_left_width;
  4015. curr_dst.h = dst_top_height;
  4016. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4017. return false;
  4018. }
  4019. // Upper-right corner
  4020. curr_src.x = srcrect->x + srcrect->w - right_width;
  4021. curr_src.w = right_width;
  4022. curr_dst.x = dstrect->x + dstrect->w - dst_right_width;
  4023. curr_dst.w = dst_right_width;
  4024. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4025. return false;
  4026. }
  4027. // Lower-right corner
  4028. curr_src.y = srcrect->y + srcrect->h - bottom_height;
  4029. curr_src.h = bottom_height;
  4030. curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height;
  4031. curr_dst.h = dst_bottom_height;
  4032. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4033. return false;
  4034. }
  4035. // Lower-left corner
  4036. curr_src.x = srcrect->x;
  4037. curr_src.w = left_width;
  4038. curr_dst.x = dstrect->x;
  4039. curr_dst.w = dst_left_width;
  4040. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4041. return false;
  4042. }
  4043. // Left
  4044. curr_src.y = srcrect->y + top_height;
  4045. curr_src.h = srcrect->h - top_height - bottom_height;
  4046. curr_dst.y = dstrect->y + dst_top_height;
  4047. curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height;
  4048. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4049. return false;
  4050. }
  4051. // Right
  4052. curr_src.x = srcrect->x + srcrect->w - right_width;
  4053. curr_src.w = right_width;
  4054. curr_dst.x = dstrect->x + dstrect->w - dst_right_width;
  4055. curr_dst.w = dst_right_width;
  4056. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4057. return false;
  4058. }
  4059. // Top
  4060. curr_src.x = srcrect->x + left_width;
  4061. curr_src.y = srcrect->y;
  4062. curr_src.w = srcrect->w - left_width - right_width;
  4063. curr_src.h = top_height;
  4064. curr_dst.x = dstrect->x + dst_left_width;
  4065. curr_dst.y = dstrect->y;
  4066. curr_dst.w = dstrect->w - dst_left_width - dst_right_width;
  4067. curr_dst.h = dst_top_height;
  4068. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4069. return false;
  4070. }
  4071. // Bottom
  4072. curr_src.y = srcrect->y + srcrect->h - bottom_height;
  4073. curr_src.h = bottom_height;
  4074. curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height;
  4075. curr_dst.h = dst_bottom_height;
  4076. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4077. return false;
  4078. }
  4079. return true;
  4080. }
  4081. bool SDL_RenderTexture9GridTiled(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float left_width, float right_width, float top_height, float bottom_height, float scale, const SDL_FRect *dstrect, float tileScale)
  4082. {
  4083. SDL_FRect full_src, full_dst;
  4084. SDL_FRect curr_src, curr_dst;
  4085. float dst_left_width;
  4086. float dst_right_width;
  4087. float dst_top_height;
  4088. float dst_bottom_height;
  4089. CHECK_RENDERER_MAGIC(renderer, false);
  4090. CHECK_TEXTURE_MAGIC(texture, false);
  4091. CHECK_PARAM(renderer != texture->renderer) {
  4092. return SDL_SetError("Texture was not created with this renderer");
  4093. }
  4094. if (!srcrect) {
  4095. full_src.x = 0;
  4096. full_src.y = 0;
  4097. full_src.w = (float)texture->w;
  4098. full_src.h = (float)texture->h;
  4099. srcrect = &full_src;
  4100. }
  4101. if (!dstrect) {
  4102. GetRenderViewportSize(renderer, &full_dst);
  4103. dstrect = &full_dst;
  4104. }
  4105. if (scale <= 0.0f || scale == 1.0f) {
  4106. dst_left_width = SDL_ceilf(left_width);
  4107. dst_right_width = SDL_ceilf(right_width);
  4108. dst_top_height = SDL_ceilf(top_height);
  4109. dst_bottom_height = SDL_ceilf(bottom_height);
  4110. } else {
  4111. dst_left_width = SDL_ceilf(left_width * scale);
  4112. dst_right_width = SDL_ceilf(right_width * scale);
  4113. dst_top_height = SDL_ceilf(top_height * scale);
  4114. dst_bottom_height = SDL_ceilf(bottom_height * scale);
  4115. }
  4116. // Center
  4117. curr_src.x = srcrect->x + left_width;
  4118. curr_src.y = srcrect->y + top_height;
  4119. curr_src.w = srcrect->w - left_width - right_width;
  4120. curr_src.h = srcrect->h - top_height - bottom_height;
  4121. curr_dst.x = dstrect->x + dst_left_width;
  4122. curr_dst.y = dstrect->y + dst_top_height;
  4123. curr_dst.w = dstrect->w - dst_left_width - dst_right_width;
  4124. curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height;
  4125. if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) {
  4126. return false;
  4127. }
  4128. // Upper-left corner
  4129. curr_src.x = srcrect->x;
  4130. curr_src.y = srcrect->y;
  4131. curr_src.w = left_width;
  4132. curr_src.h = top_height;
  4133. curr_dst.x = dstrect->x;
  4134. curr_dst.y = dstrect->y;
  4135. curr_dst.w = dst_left_width;
  4136. curr_dst.h = dst_top_height;
  4137. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4138. return false;
  4139. }
  4140. // Upper-right corner
  4141. curr_src.x = srcrect->x + srcrect->w - right_width;
  4142. curr_src.w = right_width;
  4143. curr_dst.x = dstrect->x + dstrect->w - dst_right_width;
  4144. curr_dst.w = dst_right_width;
  4145. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4146. return false;
  4147. }
  4148. // Lower-right corner
  4149. curr_src.y = srcrect->y + srcrect->h - bottom_height;
  4150. curr_src.h = bottom_height;
  4151. curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height;
  4152. curr_dst.h = dst_bottom_height;
  4153. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4154. return false;
  4155. }
  4156. // Lower-left corner
  4157. curr_src.x = srcrect->x;
  4158. curr_src.w = left_width;
  4159. curr_dst.x = dstrect->x;
  4160. curr_dst.w = dst_left_width;
  4161. if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) {
  4162. return false;
  4163. }
  4164. // Left
  4165. curr_src.y = srcrect->y + top_height;
  4166. curr_src.h = srcrect->h - top_height - bottom_height;
  4167. curr_dst.y = dstrect->y + dst_top_height;
  4168. curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height;
  4169. if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) {
  4170. return false;
  4171. }
  4172. // Right
  4173. curr_src.x = srcrect->x + srcrect->w - right_width;
  4174. curr_src.w = right_width;
  4175. curr_dst.x = dstrect->x + dstrect->w - dst_right_width;
  4176. curr_dst.w = dst_right_width;
  4177. if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) {
  4178. return false;
  4179. }
  4180. // Top
  4181. curr_src.x = srcrect->x + left_width;
  4182. curr_src.y = srcrect->y;
  4183. curr_src.w = srcrect->w - left_width - right_width;
  4184. curr_src.h = top_height;
  4185. curr_dst.x = dstrect->x + dst_left_width;
  4186. curr_dst.y = dstrect->y;
  4187. curr_dst.w = dstrect->w - dst_left_width - dst_right_width;
  4188. curr_dst.h = dst_top_height;
  4189. if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) {
  4190. return false;
  4191. }
  4192. // Bottom
  4193. curr_src.y = srcrect->y + srcrect->h - bottom_height;
  4194. curr_src.h = bottom_height;
  4195. curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height;
  4196. curr_dst.h = dst_bottom_height;
  4197. if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) {
  4198. return false;
  4199. }
  4200. return true;
  4201. }
  4202. bool SDL_RenderGeometry(SDL_Renderer *renderer,
  4203. SDL_Texture *texture,
  4204. const SDL_Vertex *vertices, int num_vertices,
  4205. const int *indices, int num_indices)
  4206. {
  4207. if (vertices) {
  4208. const float *xy = &vertices->position.x;
  4209. int xy_stride = sizeof(SDL_Vertex);
  4210. const SDL_FColor *color = &vertices->color;
  4211. int color_stride = sizeof(SDL_Vertex);
  4212. const float *uv = &vertices->tex_coord.x;
  4213. int uv_stride = sizeof(SDL_Vertex);
  4214. int size_indices = 4;
  4215. return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices, indices, num_indices, size_indices);
  4216. } else {
  4217. return SDL_InvalidParamError("vertices");
  4218. }
  4219. }
  4220. #ifdef SDL_VIDEO_RENDER_SW
  4221. static int remap_one_indice(
  4222. int prev,
  4223. int k,
  4224. SDL_Texture *texture,
  4225. const float *xy, int xy_stride,
  4226. const SDL_FColor *color, int color_stride,
  4227. const float *uv, int uv_stride)
  4228. {
  4229. const float *xy0_, *xy1_, *uv0_, *uv1_;
  4230. const SDL_FColor *col0_, *col1_;
  4231. xy0_ = (const float *)((const char *)xy + prev * xy_stride);
  4232. xy1_ = (const float *)((const char *)xy + k * xy_stride);
  4233. if (xy0_[0] != xy1_[0]) {
  4234. return k;
  4235. }
  4236. if (xy0_[1] != xy1_[1]) {
  4237. return k;
  4238. }
  4239. if (texture) {
  4240. uv0_ = (const float *)((const char *)uv + prev * uv_stride);
  4241. uv1_ = (const float *)((const char *)uv + k * uv_stride);
  4242. if (uv0_[0] != uv1_[0]) {
  4243. return k;
  4244. }
  4245. if (uv0_[1] != uv1_[1]) {
  4246. return k;
  4247. }
  4248. }
  4249. col0_ = (const SDL_FColor *)((const char *)color + prev * color_stride);
  4250. col1_ = (const SDL_FColor *)((const char *)color + k * color_stride);
  4251. if (SDL_memcmp(col0_, col1_, sizeof(*col0_)) != 0) {
  4252. return k;
  4253. }
  4254. return prev;
  4255. }
  4256. static int remap_indices(
  4257. int prev[3],
  4258. int k,
  4259. SDL_Texture *texture,
  4260. const float *xy, int xy_stride,
  4261. const SDL_FColor *color, int color_stride,
  4262. const float *uv, int uv_stride)
  4263. {
  4264. int i;
  4265. if (prev[0] == -1) {
  4266. return k;
  4267. }
  4268. for (i = 0; i < 3; i++) {
  4269. int new_k = remap_one_indice(prev[i], k, texture, xy, xy_stride, color, color_stride, uv, uv_stride);
  4270. if (new_k != k) {
  4271. return new_k;
  4272. }
  4273. }
  4274. return k;
  4275. }
  4276. #define DEBUG_SW_RENDER_GEOMETRY 0
  4277. // For the software renderer, try to reinterpret triangles as SDL_Rect
  4278. static bool SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer,
  4279. SDL_Texture *texture,
  4280. const float *xy, int xy_stride,
  4281. const SDL_FColor *color, int color_stride,
  4282. const float *uv, int uv_stride,
  4283. int num_vertices,
  4284. const void *indices, int num_indices, int size_indices)
  4285. {
  4286. int i;
  4287. bool result = true;
  4288. int count = indices ? num_indices : num_vertices;
  4289. int prev[3]; // Previous triangle vertex indices
  4290. float texw = 0.0f, texh = 0.0f;
  4291. SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
  4292. float r = 0, g = 0, b = 0, a = 0;
  4293. const SDL_RenderViewState *view = renderer->view;
  4294. const float scale_x = view->current_scale.x;
  4295. const float scale_y = view->current_scale.y;
  4296. // Save
  4297. SDL_GetRenderDrawBlendMode(renderer, &blendMode);
  4298. SDL_GetRenderDrawColorFloat(renderer, &r, &g, &b, &a);
  4299. if (texture) {
  4300. SDL_GetTextureSize(texture, &texw, &texh);
  4301. }
  4302. prev[0] = -1;
  4303. prev[1] = -1;
  4304. prev[2] = -1;
  4305. size_indices = indices ? size_indices : 0;
  4306. for (i = 0; i < count; i += 3) {
  4307. int k0, k1, k2; // Current triangle indices
  4308. int is_quad = 1;
  4309. #if DEBUG_SW_RENDER_GEOMETRY
  4310. int is_uniform = 1;
  4311. int is_rectangle = 1;
  4312. #endif
  4313. int A = -1; // Top left vertex
  4314. int B = -1; // Bottom right vertex
  4315. int C = -1; // Third vertex of current triangle
  4316. int C2 = -1; // Last, vertex of previous triangle
  4317. if (size_indices == 4) {
  4318. k0 = ((const Uint32 *)indices)[i];
  4319. k1 = ((const Uint32 *)indices)[i + 1];
  4320. k2 = ((const Uint32 *)indices)[i + 2];
  4321. } else if (size_indices == 2) {
  4322. k0 = ((const Uint16 *)indices)[i];
  4323. k1 = ((const Uint16 *)indices)[i + 1];
  4324. k2 = ((const Uint16 *)indices)[i + 2];
  4325. } else if (size_indices == 1) {
  4326. k0 = ((const Uint8 *)indices)[i];
  4327. k1 = ((const Uint8 *)indices)[i + 1];
  4328. k2 = ((const Uint8 *)indices)[i + 2];
  4329. } else {
  4330. /* Vertices were not provided by indices. Maybe some are duplicated.
  4331. * We try to indentificate the duplicates by comparing with the previous three vertices */
  4332. k0 = remap_indices(prev, i, texture, xy, xy_stride, color, color_stride, uv, uv_stride);
  4333. k1 = remap_indices(prev, i + 1, texture, xy, xy_stride, color, color_stride, uv, uv_stride);
  4334. k2 = remap_indices(prev, i + 2, texture, xy, xy_stride, color, color_stride, uv, uv_stride);
  4335. }
  4336. if (prev[0] == -1) {
  4337. prev[0] = k0;
  4338. prev[1] = k1;
  4339. prev[2] = k2;
  4340. continue;
  4341. }
  4342. /* Two triangles forming a quadrilateral,
  4343. * prev and current triangles must have exactly 2 common vertices */
  4344. {
  4345. int cnt = 0, j = 3;
  4346. while (j--) {
  4347. int p = prev[j];
  4348. if (p == k0 || p == k1 || p == k2) {
  4349. cnt++;
  4350. }
  4351. }
  4352. is_quad = (cnt == 2);
  4353. }
  4354. // Identify vertices
  4355. if (is_quad) {
  4356. const float *xy0_, *xy1_, *xy2_;
  4357. float x0, x1, x2;
  4358. float y0, y1, y2;
  4359. xy0_ = (const float *)((const char *)xy + k0 * xy_stride);
  4360. xy1_ = (const float *)((const char *)xy + k1 * xy_stride);
  4361. xy2_ = (const float *)((const char *)xy + k2 * xy_stride);
  4362. x0 = xy0_[0];
  4363. y0 = xy0_[1];
  4364. x1 = xy1_[0];
  4365. y1 = xy1_[1];
  4366. x2 = xy2_[0];
  4367. y2 = xy2_[1];
  4368. // Find top-left
  4369. if (x0 <= x1 && y0 <= y1) {
  4370. if (x0 <= x2 && y0 <= y2) {
  4371. A = k0;
  4372. } else {
  4373. A = k2;
  4374. }
  4375. } else {
  4376. if (x1 <= x2 && y1 <= y2) {
  4377. A = k1;
  4378. } else {
  4379. A = k2;
  4380. }
  4381. }
  4382. // Find bottom-right
  4383. if (x0 >= x1 && y0 >= y1) {
  4384. if (x0 >= x2 && y0 >= y2) {
  4385. B = k0;
  4386. } else {
  4387. B = k2;
  4388. }
  4389. } else {
  4390. if (x1 >= x2 && y1 >= y2) {
  4391. B = k1;
  4392. } else {
  4393. B = k2;
  4394. }
  4395. }
  4396. // Find C
  4397. if (k0 != A && k0 != B) {
  4398. C = k0;
  4399. } else if (k1 != A && k1 != B) {
  4400. C = k1;
  4401. } else {
  4402. C = k2;
  4403. }
  4404. // Find C2
  4405. if (prev[0] != A && prev[0] != B) {
  4406. C2 = prev[0];
  4407. } else if (prev[1] != A && prev[1] != B) {
  4408. C2 = prev[1];
  4409. } else {
  4410. C2 = prev[2];
  4411. }
  4412. xy0_ = (const float *)((const char *)xy + A * xy_stride);
  4413. xy1_ = (const float *)((const char *)xy + B * xy_stride);
  4414. xy2_ = (const float *)((const char *)xy + C * xy_stride);
  4415. x0 = xy0_[0];
  4416. y0 = xy0_[1];
  4417. x1 = xy1_[0];
  4418. y1 = xy1_[1];
  4419. x2 = xy2_[0];
  4420. y2 = xy2_[1];
  4421. // Check if triangle A B C is rectangle
  4422. if ((x0 == x2 && y1 == y2) || (y0 == y2 && x1 == x2)) {
  4423. // ok
  4424. } else {
  4425. is_quad = 0;
  4426. #if DEBUG_SW_RENDER_GEOMETRY
  4427. is_rectangle = 0;
  4428. #endif
  4429. }
  4430. xy2_ = (const float *)((const char *)xy + C2 * xy_stride);
  4431. x2 = xy2_[0];
  4432. y2 = xy2_[1];
  4433. // Check if triangle A B C2 is rectangle
  4434. if ((x0 == x2 && y1 == y2) || (y0 == y2 && x1 == x2)) {
  4435. // ok
  4436. } else {
  4437. is_quad = 0;
  4438. #if DEBUG_SW_RENDER_GEOMETRY
  4439. is_rectangle = 0;
  4440. #endif
  4441. }
  4442. }
  4443. // Check if uniformly colored
  4444. if (is_quad) {
  4445. const SDL_FColor *col0_ = (const SDL_FColor *)((const char *)color + A * color_stride);
  4446. const SDL_FColor *col1_ = (const SDL_FColor *)((const char *)color + B * color_stride);
  4447. const SDL_FColor *col2_ = (const SDL_FColor *)((const char *)color + C * color_stride);
  4448. const SDL_FColor *col3_ = (const SDL_FColor *)((const char *)color + C2 * color_stride);
  4449. if (SDL_memcmp(col0_, col1_, sizeof(*col0_)) == 0 &&
  4450. SDL_memcmp(col0_, col2_, sizeof(*col0_)) == 0 &&
  4451. SDL_memcmp(col0_, col3_, sizeof(*col0_)) == 0) {
  4452. // ok
  4453. } else {
  4454. is_quad = 0;
  4455. #if DEBUG_SW_RENDER_GEOMETRY
  4456. is_uniform = 0;
  4457. #endif
  4458. }
  4459. }
  4460. // Check if UVs within range
  4461. if (is_quad && uv) {
  4462. const float *uv0_ = (const float *)((const char *)uv + A * color_stride);
  4463. const float *uv1_ = (const float *)((const char *)uv + B * color_stride);
  4464. const float *uv2_ = (const float *)((const char *)uv + C * color_stride);
  4465. const float *uv3_ = (const float *)((const char *)uv + C2 * color_stride);
  4466. if (uv0_[0] >= 0.0f && uv0_[0] <= 1.0f &&
  4467. uv1_[0] >= 0.0f && uv1_[0] <= 1.0f &&
  4468. uv2_[0] >= 0.0f && uv2_[0] <= 1.0f &&
  4469. uv3_[0] >= 0.0f && uv3_[0] <= 1.0f) {
  4470. // ok
  4471. } else {
  4472. is_quad = 0;
  4473. }
  4474. }
  4475. // Start rendering rect
  4476. if (is_quad) {
  4477. SDL_FRect s;
  4478. SDL_FRect d;
  4479. const float *xy0_, *xy1_, *uv0_, *uv1_;
  4480. const SDL_FColor *col0_ = (const SDL_FColor *)((const char *)color + k0 * color_stride);
  4481. xy0_ = (const float *)((const char *)xy + A * xy_stride);
  4482. xy1_ = (const float *)((const char *)xy + B * xy_stride);
  4483. if (texture) {
  4484. uv0_ = (const float *)((const char *)uv + A * uv_stride);
  4485. uv1_ = (const float *)((const char *)uv + B * uv_stride);
  4486. s.x = uv0_[0] * texw;
  4487. s.y = uv0_[1] * texh;
  4488. s.w = uv1_[0] * texw - s.x;
  4489. s.h = uv1_[1] * texh - s.y;
  4490. } else {
  4491. s.x = s.y = s.w = s.h = 0;
  4492. }
  4493. d.x = xy0_[0];
  4494. d.y = xy0_[1];
  4495. d.w = xy1_[0] - d.x;
  4496. d.h = xy1_[1] - d.y;
  4497. // Rect + texture
  4498. if (texture && s.w != 0 && s.h != 0) {
  4499. SDL_SetTextureAlphaModFloat(texture, col0_->a);
  4500. SDL_SetTextureColorModFloat(texture, col0_->r, col0_->g, col0_->b);
  4501. if (s.w > 0 && s.h > 0) {
  4502. SDL_RenderTexture(renderer, texture, &s, &d);
  4503. } else {
  4504. int flags = 0;
  4505. if (s.w < 0) {
  4506. flags |= SDL_FLIP_HORIZONTAL;
  4507. s.w *= -1;
  4508. s.x -= s.w;
  4509. }
  4510. if (s.h < 0) {
  4511. flags |= SDL_FLIP_VERTICAL;
  4512. s.h *= -1;
  4513. s.y -= s.h;
  4514. }
  4515. SDL_RenderTextureRotated(renderer, texture, &s, &d, 0, NULL, (SDL_FlipMode)flags);
  4516. }
  4517. #if DEBUG_SW_RENDER_GEOMETRY
  4518. SDL_Log("Rect-COPY: RGB %f %f %f - Alpha:%f - texture=%p: src=(%d,%d, %d x %d) dst (%f, %f, %f x %f)", col0_->r, col0_->g, col0_->b, col0_->a,
  4519. (void *)texture, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h);
  4520. #endif
  4521. } else if (d.w != 0.0f && d.h != 0.0f) { // Rect, no texture
  4522. SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
  4523. SDL_SetRenderDrawColorFloat(renderer, col0_->r, col0_->g, col0_->b, col0_->a);
  4524. SDL_RenderFillRect(renderer, &d);
  4525. #if DEBUG_SW_RENDER_GEOMETRY
  4526. SDL_Log("Rect-FILL: RGB %f %f %f - Alpha:%f - texture=%p: dst (%f, %f, %f x %f)", col0_->r, col0_->g, col0_->b, col0_->a,
  4527. (void *)texture, d.x, d.y, d.w, d.h);
  4528. } else {
  4529. SDL_Log("Rect-DISMISS: RGB %f %f %f - Alpha:%f - texture=%p: src=(%d,%d, %d x %d) dst (%f, %f, %f x %f)", col0_->r, col0_->g, col0_->b, col0_->a,
  4530. (void *)texture, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h);
  4531. #endif
  4532. }
  4533. prev[0] = -1;
  4534. } else {
  4535. // Render triangles
  4536. if (prev[0] != -1) {
  4537. #if DEBUG_SW_RENDER_GEOMETRY
  4538. SDL_Log("Triangle %d %d %d - is_uniform:%d is_rectangle:%d", prev[0], prev[1], prev[2], is_uniform, is_rectangle);
  4539. #endif
  4540. result = QueueCmdGeometry(renderer, texture,
  4541. xy, xy_stride, color, color_stride, uv, uv_stride,
  4542. num_vertices, prev, 3, 4,
  4543. scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
  4544. if (!result) {
  4545. goto end;
  4546. }
  4547. }
  4548. prev[0] = k0;
  4549. prev[1] = k1;
  4550. prev[2] = k2;
  4551. }
  4552. } // End for (), next triangle
  4553. if (prev[0] != -1) {
  4554. // flush the last triangle
  4555. #if DEBUG_SW_RENDER_GEOMETRY
  4556. SDL_Log("Last triangle %d %d %d", prev[0], prev[1], prev[2]);
  4557. #endif
  4558. result = QueueCmdGeometry(renderer, texture,
  4559. xy, xy_stride, color, color_stride, uv, uv_stride,
  4560. num_vertices, prev, 3, 4,
  4561. scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
  4562. if (!result) {
  4563. goto end;
  4564. }
  4565. }
  4566. end:
  4567. // Restore
  4568. SDL_SetRenderDrawBlendMode(renderer, blendMode);
  4569. SDL_SetRenderDrawColorFloat(renderer, r, g, b, a);
  4570. return result;
  4571. }
  4572. #endif // SDL_VIDEO_RENDER_SW
  4573. bool SDL_RenderGeometryRaw(SDL_Renderer *renderer,
  4574. SDL_Texture *texture,
  4575. const float *xy, int xy_stride,
  4576. const SDL_FColor *color, int color_stride,
  4577. const float *uv, int uv_stride,
  4578. int num_vertices,
  4579. const void *indices, int num_indices, int size_indices)
  4580. {
  4581. int i;
  4582. int count = indices ? num_indices : num_vertices;
  4583. SDL_TextureAddressMode texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP;
  4584. SDL_TextureAddressMode texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP;
  4585. CHECK_RENDERER_MAGIC(renderer, false);
  4586. if (texture) {
  4587. CHECK_TEXTURE_MAGIC(texture, false);
  4588. CHECK_PARAM(renderer != texture->renderer) {
  4589. return SDL_SetError("Texture was not created with this renderer");
  4590. }
  4591. }
  4592. CHECK_PARAM(!xy) {
  4593. return SDL_InvalidParamError("xy");
  4594. }
  4595. CHECK_PARAM(!color) {
  4596. return SDL_InvalidParamError("color");
  4597. }
  4598. CHECK_PARAM(texture && !uv) {
  4599. return SDL_InvalidParamError("uv");
  4600. }
  4601. (void)count; // In case parameter checking is disabled
  4602. CHECK_PARAM(count % 3 != 0) {
  4603. return SDL_InvalidParamError(indices ? "num_indices" : "num_vertices");
  4604. }
  4605. if (indices) {
  4606. CHECK_PARAM(size_indices != 1 && size_indices != 2 && size_indices != 4) {
  4607. return SDL_InvalidParamError("size_indices");
  4608. }
  4609. }
  4610. if (!indices) {
  4611. size_indices = 0;
  4612. }
  4613. if (!renderer->QueueGeometry) {
  4614. return SDL_Unsupported();
  4615. }
  4616. if (num_vertices < 3) {
  4617. return true;
  4618. }
  4619. if (texture) {
  4620. if (!UpdateTexturePalette(texture)) {
  4621. return false;
  4622. }
  4623. if (texture->native) {
  4624. texture = texture->native;
  4625. }
  4626. if (renderer->npot_texture_wrap_unsupported && IsNPOT(texture->w)) {
  4627. texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP;
  4628. } else {
  4629. texture_address_mode_u = renderer->texture_address_mode_u;
  4630. }
  4631. if (renderer->npot_texture_wrap_unsupported && IsNPOT(texture->h)) {
  4632. texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP;
  4633. } else {
  4634. texture_address_mode_v = renderer->texture_address_mode_v;
  4635. }
  4636. if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_AUTO ||
  4637. texture_address_mode_v == SDL_TEXTURE_ADDRESS_AUTO) {
  4638. for (i = 0; i < num_vertices; ++i) {
  4639. const float *uv_ = (const float *)((const char *)uv + i * uv_stride);
  4640. float u = uv_[0];
  4641. float v = uv_[1];
  4642. if (u < 0.0f || u > 1.0f) {
  4643. if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_AUTO) {
  4644. texture_address_mode_u = SDL_TEXTURE_ADDRESS_WRAP;
  4645. if (texture_address_mode_v != SDL_TEXTURE_ADDRESS_AUTO) {
  4646. break;
  4647. }
  4648. }
  4649. }
  4650. if (v < 0.0f || v > 1.0f) {
  4651. if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_AUTO) {
  4652. texture_address_mode_v = SDL_TEXTURE_ADDRESS_WRAP;
  4653. if (texture_address_mode_u != SDL_TEXTURE_ADDRESS_AUTO) {
  4654. break;
  4655. }
  4656. }
  4657. }
  4658. }
  4659. if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_AUTO) {
  4660. texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP;
  4661. }
  4662. if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_AUTO) {
  4663. texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP;
  4664. }
  4665. }
  4666. }
  4667. if (indices) {
  4668. for (i = 0; i < num_indices; ++i) {
  4669. int j;
  4670. if (size_indices == 4) {
  4671. j = ((const Uint32 *)indices)[i];
  4672. } else if (size_indices == 2) {
  4673. j = ((const Uint16 *)indices)[i];
  4674. } else {
  4675. j = ((const Uint8 *)indices)[i];
  4676. }
  4677. if (j < 0 || j >= num_vertices) {
  4678. return SDL_SetError("Values of 'indices' out of bounds");
  4679. }
  4680. }
  4681. }
  4682. if (texture) {
  4683. texture->last_command_generation = renderer->render_command_generation;
  4684. }
  4685. // For the software renderer, try to reinterpret triangles as SDL_Rect
  4686. #ifdef SDL_VIDEO_RENDER_SW
  4687. if (renderer->software &&
  4688. texture_address_mode_u == SDL_TEXTURE_ADDRESS_CLAMP &&
  4689. texture_address_mode_v == SDL_TEXTURE_ADDRESS_CLAMP) {
  4690. return SDL_SW_RenderGeometryRaw(renderer, texture,
  4691. xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices,
  4692. indices, num_indices, size_indices);
  4693. }
  4694. #endif
  4695. const SDL_RenderViewState *view = renderer->view;
  4696. return QueueCmdGeometry(renderer, texture,
  4697. xy, xy_stride, color, color_stride, uv, uv_stride,
  4698. num_vertices, indices, num_indices, size_indices,
  4699. view->current_scale.x, view->current_scale.y,
  4700. texture_address_mode_u, texture_address_mode_v);
  4701. }
  4702. bool SDL_SetRenderTextureAddressMode(SDL_Renderer *renderer, SDL_TextureAddressMode u_mode, SDL_TextureAddressMode v_mode)
  4703. {
  4704. CHECK_RENDERER_MAGIC(renderer, false);
  4705. renderer->texture_address_mode_u = u_mode;
  4706. renderer->texture_address_mode_v = v_mode;
  4707. return true;
  4708. }
  4709. bool SDL_GetRenderTextureAddressMode(SDL_Renderer *renderer, SDL_TextureAddressMode *u_mode, SDL_TextureAddressMode *v_mode)
  4710. {
  4711. if (u_mode) {
  4712. *u_mode = SDL_TEXTURE_ADDRESS_INVALID;
  4713. }
  4714. if (v_mode) {
  4715. *v_mode = SDL_TEXTURE_ADDRESS_INVALID;
  4716. }
  4717. CHECK_RENDERER_MAGIC(renderer, false);
  4718. if (u_mode) {
  4719. *u_mode = renderer->texture_address_mode_u;
  4720. }
  4721. if (v_mode) {
  4722. *v_mode = renderer->texture_address_mode_v;
  4723. }
  4724. return true;
  4725. }
  4726. SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
  4727. {
  4728. CHECK_RENDERER_MAGIC(renderer, NULL);
  4729. if (!renderer->RenderReadPixels) {
  4730. SDL_Unsupported();
  4731. return NULL;
  4732. }
  4733. FlushRenderCommands(renderer); // we need to render before we read the results.
  4734. SDL_Rect real_rect = renderer->view->pixel_viewport;
  4735. if (rect) {
  4736. if (!SDL_GetRectIntersection(rect, &real_rect, &real_rect)) {
  4737. SDL_SetError("Can't read outside the current viewport");
  4738. return NULL;
  4739. }
  4740. }
  4741. SDL_Surface *surface = renderer->RenderReadPixels(renderer, &real_rect);
  4742. if (surface) {
  4743. SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
  4744. if (renderer->target) {
  4745. SDL_Texture *target = renderer->target;
  4746. SDL_Texture *parent = SDL_GetPointerProperty(SDL_GetTextureProperties(target), SDL_PROP_TEXTURE_PARENT_POINTER, NULL);
  4747. SDL_PixelFormat expected_format = (parent ? parent->format : target->format);
  4748. if (SDL_COLORSPACETRANSFER(target->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
  4749. SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point * SCRGB_NITS);
  4750. } else {
  4751. SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point);
  4752. }
  4753. SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, target->HDR_headroom);
  4754. // Set the expected surface format
  4755. if ((surface->format == SDL_PIXELFORMAT_ARGB8888 && expected_format == SDL_PIXELFORMAT_XRGB8888) ||
  4756. (surface->format == SDL_PIXELFORMAT_RGBA8888 && expected_format == SDL_PIXELFORMAT_RGBX8888) ||
  4757. (surface->format == SDL_PIXELFORMAT_ABGR8888 && expected_format == SDL_PIXELFORMAT_XBGR8888) ||
  4758. (surface->format == SDL_PIXELFORMAT_BGRA8888 && expected_format == SDL_PIXELFORMAT_BGRX8888)) {
  4759. surface->format = expected_format;
  4760. surface->fmt = SDL_GetPixelFormatDetails(expected_format);
  4761. }
  4762. } else {
  4763. if (SDL_COLORSPACETRANSFER(renderer->output_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
  4764. SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point * SCRGB_NITS);
  4765. } else {
  4766. SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
  4767. }
  4768. SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->HDR_headroom);
  4769. }
  4770. }
  4771. return surface;
  4772. }
  4773. static void SDL_RenderApplyWindowShape(SDL_Renderer *renderer)
  4774. {
  4775. SDL_Surface *shape = (SDL_Surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(renderer->window), SDL_PROP_WINDOW_SHAPE_POINTER, NULL);
  4776. if (shape != renderer->shape_surface) {
  4777. if (renderer->shape_texture) {
  4778. SDL_DestroyTexture(renderer->shape_texture);
  4779. renderer->shape_texture = NULL;
  4780. }
  4781. if (shape) {
  4782. // There's nothing we can do if this fails, so just keep on going
  4783. renderer->shape_texture = SDL_CreateTextureFromSurface(renderer, shape);
  4784. SDL_SetTextureBlendMode(renderer->shape_texture,
  4785. SDL_ComposeCustomBlendMode(
  4786. SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
  4787. SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD));
  4788. }
  4789. renderer->shape_surface = shape;
  4790. }
  4791. if (renderer->shape_texture) {
  4792. SDL_RenderTexture(renderer, renderer->shape_texture, NULL, NULL);
  4793. }
  4794. }
  4795. static void SDL_SimulateRenderVSync(SDL_Renderer *renderer)
  4796. {
  4797. Uint64 now, elapsed;
  4798. const Uint64 interval = renderer->simulate_vsync_interval_ns;
  4799. if (!interval) {
  4800. // We can't do sub-ns delay, so just return here
  4801. return;
  4802. }
  4803. now = SDL_GetTicksNS();
  4804. elapsed = (now - renderer->last_present);
  4805. if (elapsed < interval) {
  4806. Uint64 duration = (interval - elapsed);
  4807. SDL_DelayPrecise(duration);
  4808. now = SDL_GetTicksNS();
  4809. }
  4810. elapsed = (now - renderer->last_present);
  4811. if (!renderer->last_present || elapsed > SDL_MS_TO_NS(1000)) {
  4812. // It's been too long, reset the presentation timeline
  4813. renderer->last_present = now;
  4814. } else {
  4815. renderer->last_present += (elapsed / interval) * interval;
  4816. }
  4817. }
  4818. bool SDL_RenderPresent(SDL_Renderer *renderer)
  4819. {
  4820. bool presented = true;
  4821. CHECK_RENDERER_MAGIC(renderer, false);
  4822. if (renderer->target) {
  4823. if (!renderer->window && SDL_strcmp(renderer->name, SDL_GPU_RENDERER) == 0) {
  4824. // We're an offscreen renderer, we must submit the command queue
  4825. } else {
  4826. return SDL_SetError("You can't present on a render target");
  4827. }
  4828. }
  4829. if (renderer->transparent_window) {
  4830. SDL_RenderApplyWindowShape(renderer);
  4831. }
  4832. FlushRenderCommands(renderer); // time to send everything to the GPU!
  4833. #if DONT_DRAW_WHILE_HIDDEN
  4834. // Don't present while we're hidden
  4835. if (renderer->hidden) {
  4836. presented = false;
  4837. } else
  4838. #endif
  4839. if (!renderer->RenderPresent(renderer)) {
  4840. presented = false;
  4841. }
  4842. if (renderer->simulate_vsync ||
  4843. (!presented && renderer->wanted_vsync)) {
  4844. SDL_SimulateRenderVSync(renderer);
  4845. }
  4846. return true;
  4847. }
  4848. static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
  4849. {
  4850. SDL_Renderer *renderer;
  4851. if (texture->public_palette) {
  4852. SDL_SetTexturePalette(texture, NULL);
  4853. }
  4854. SDL_DestroyProperties(texture->props);
  4855. renderer = texture->renderer;
  4856. if (is_destroying) {
  4857. // Renderer get destroyed, avoid to queue more commands
  4858. } else {
  4859. if (texture == renderer->target) {
  4860. SDL_SetRenderTarget(renderer, NULL); // implies command queue flush
  4861. } else {
  4862. FlushRenderCommandsIfTextureNeeded(texture);
  4863. }
  4864. }
  4865. SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, false);
  4866. if (texture->next) {
  4867. texture->next->prev = texture->prev;
  4868. }
  4869. if (texture->prev) {
  4870. texture->prev->next = texture->next;
  4871. } else {
  4872. renderer->textures = texture->next;
  4873. }
  4874. if (texture->native) {
  4875. SDL_DestroyTextureInternal(texture->native, is_destroying);
  4876. }
  4877. #ifdef SDL_HAVE_YUV
  4878. if (texture->yuv) {
  4879. SDL_SW_DestroyYUVTexture(texture->yuv);
  4880. }
  4881. #endif
  4882. SDL_free(texture->pixels);
  4883. renderer->DestroyTexture(renderer, texture);
  4884. if (texture->palette_surface) {
  4885. SDL_DestroySurface(texture->palette_surface);
  4886. texture->palette_surface = NULL;
  4887. }
  4888. if (texture->locked_surface) {
  4889. SDL_DestroySurface(texture->locked_surface);
  4890. texture->locked_surface = NULL;
  4891. }
  4892. SDL_free(texture);
  4893. }
  4894. void SDL_DestroyTexture(SDL_Texture *texture)
  4895. {
  4896. CHECK_TEXTURE_MAGIC(texture, );
  4897. if (--texture->refcount > 0) {
  4898. return;
  4899. }
  4900. SDL_DestroyTextureInternal(texture, false /* is_destroying */);
  4901. }
  4902. static void SDL_DiscardAllCommands(SDL_Renderer *renderer)
  4903. {
  4904. SDL_RenderCommand *cmd;
  4905. if (renderer->render_commands_tail) {
  4906. renderer->render_commands_tail->next = renderer->render_commands_pool;
  4907. cmd = renderer->render_commands;
  4908. } else {
  4909. cmd = renderer->render_commands_pool;
  4910. }
  4911. renderer->render_commands_pool = NULL;
  4912. renderer->render_commands_tail = NULL;
  4913. renderer->render_commands = NULL;
  4914. renderer->vertex_data_used = 0;
  4915. while (cmd) {
  4916. SDL_RenderCommand *next = cmd->next;
  4917. SDL_free(cmd);
  4918. cmd = next;
  4919. }
  4920. }
  4921. void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer)
  4922. {
  4923. SDL_assert(renderer != NULL);
  4924. SDL_assert(!renderer->destroyed);
  4925. renderer->destroyed = true;
  4926. SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer);
  4927. if (renderer->window) {
  4928. SDL_PropertiesID props = SDL_GetWindowProperties(renderer->window);
  4929. if (SDL_GetPointerProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER, NULL) == renderer) {
  4930. SDL_ClearProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER);
  4931. }
  4932. SDL_RemoveWindowRenderer(renderer->window, renderer);
  4933. }
  4934. if (renderer->software) {
  4935. // Make sure all drawing to a surface is complete
  4936. FlushRenderCommands(renderer);
  4937. }
  4938. SDL_DiscardAllCommands(renderer);
  4939. if (renderer->debug_char_texture_atlas) {
  4940. SDL_DestroyTexture(renderer->debug_char_texture_atlas);
  4941. renderer->debug_char_texture_atlas = NULL;
  4942. }
  4943. // Free existing textures for this renderer
  4944. while (renderer->textures) {
  4945. SDL_Texture *tex = renderer->textures;
  4946. SDL_DestroyTextureInternal(renderer->textures, true /* is_destroying */);
  4947. SDL_assert(tex != renderer->textures); // satisfy static analysis.
  4948. }
  4949. // Free palette cache, which should be empty now
  4950. if (renderer->palettes) {
  4951. SDL_assert(SDL_HashTableEmpty(renderer->palettes));
  4952. SDL_DestroyHashTable(renderer->palettes);
  4953. renderer->palettes = NULL;
  4954. }
  4955. // Clean up renderer-specific resources
  4956. if (renderer->DestroyRenderer) {
  4957. renderer->DestroyRenderer(renderer);
  4958. }
  4959. if (renderer->target_mutex) {
  4960. SDL_DestroyMutex(renderer->target_mutex);
  4961. renderer->target_mutex = NULL;
  4962. }
  4963. if (renderer->vertex_data) {
  4964. SDL_free(renderer->vertex_data);
  4965. renderer->vertex_data = NULL;
  4966. }
  4967. if (renderer->texture_formats) {
  4968. SDL_free(renderer->texture_formats);
  4969. renderer->texture_formats = NULL;
  4970. }
  4971. if (renderer->props) {
  4972. SDL_DestroyProperties(renderer->props);
  4973. renderer->props = 0;
  4974. }
  4975. }
  4976. void SDL_DestroyRenderer(SDL_Renderer *renderer)
  4977. {
  4978. CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer,);
  4979. // if we've already destroyed the renderer through SDL_DestroyWindow, we just need
  4980. // to free the renderer pointer. This lets apps destroy the window and renderer
  4981. // in either order.
  4982. if (!renderer->destroyed) {
  4983. SDL_DestroyRendererWithoutFreeing(renderer);
  4984. }
  4985. SDL_Renderer *curr = SDL_renderers;
  4986. SDL_Renderer *prev = NULL;
  4987. while (curr) {
  4988. if (curr == renderer) {
  4989. if (prev) {
  4990. prev->next = renderer->next;
  4991. } else {
  4992. SDL_renderers = renderer->next;
  4993. }
  4994. break;
  4995. }
  4996. prev = curr;
  4997. curr = curr->next;
  4998. }
  4999. SDL_SetObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER, false); // It's no longer magical...
  5000. SDL_free(renderer);
  5001. }
  5002. void *SDL_GetRenderMetalLayer(SDL_Renderer *renderer)
  5003. {
  5004. CHECK_RENDERER_MAGIC(renderer, NULL);
  5005. if (renderer->GetMetalLayer) {
  5006. FlushRenderCommands(renderer); // in case the app is going to mess with it.
  5007. return renderer->GetMetalLayer(renderer);
  5008. }
  5009. return NULL;
  5010. }
  5011. void *SDL_GetRenderMetalCommandEncoder(SDL_Renderer *renderer)
  5012. {
  5013. CHECK_RENDERER_MAGIC(renderer, NULL);
  5014. if (renderer->GetMetalCommandEncoder) {
  5015. FlushRenderCommands(renderer); // in case the app is going to mess with it.
  5016. return renderer->GetMetalCommandEncoder(renderer);
  5017. }
  5018. return NULL;
  5019. }
  5020. bool SDL_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore)
  5021. {
  5022. CHECK_RENDERER_MAGIC(renderer, false);
  5023. if (!renderer->AddVulkanRenderSemaphores) {
  5024. return SDL_Unsupported();
  5025. }
  5026. return renderer->AddVulkanRenderSemaphores(renderer, wait_stage_mask, wait_semaphore, signal_semaphore);
  5027. }
  5028. static SDL_BlendMode SDL_GetShortBlendMode(SDL_BlendMode blendMode)
  5029. {
  5030. if (blendMode == SDL_BLENDMODE_NONE_FULL) {
  5031. return SDL_BLENDMODE_NONE;
  5032. }
  5033. if (blendMode == SDL_BLENDMODE_BLEND_FULL) {
  5034. return SDL_BLENDMODE_BLEND;
  5035. }
  5036. if (blendMode == SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL) {
  5037. return SDL_BLENDMODE_BLEND_PREMULTIPLIED;
  5038. }
  5039. if (blendMode == SDL_BLENDMODE_ADD_FULL) {
  5040. return SDL_BLENDMODE_ADD;
  5041. }
  5042. if (blendMode == SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL) {
  5043. return SDL_BLENDMODE_ADD_PREMULTIPLIED;
  5044. }
  5045. if (blendMode == SDL_BLENDMODE_MOD_FULL) {
  5046. return SDL_BLENDMODE_MOD;
  5047. }
  5048. if (blendMode == SDL_BLENDMODE_MUL_FULL) {
  5049. return SDL_BLENDMODE_MUL;
  5050. }
  5051. return blendMode;
  5052. }
  5053. static SDL_BlendMode SDL_GetLongBlendMode(SDL_BlendMode blendMode)
  5054. {
  5055. if (blendMode == SDL_BLENDMODE_NONE) {
  5056. return SDL_BLENDMODE_NONE_FULL;
  5057. }
  5058. if (blendMode == SDL_BLENDMODE_BLEND) {
  5059. return SDL_BLENDMODE_BLEND_FULL;
  5060. }
  5061. if (blendMode == SDL_BLENDMODE_BLEND_PREMULTIPLIED) {
  5062. return SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL;
  5063. }
  5064. if (blendMode == SDL_BLENDMODE_ADD) {
  5065. return SDL_BLENDMODE_ADD_FULL;
  5066. }
  5067. if (blendMode == SDL_BLENDMODE_ADD_PREMULTIPLIED) {
  5068. return SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL;
  5069. }
  5070. if (blendMode == SDL_BLENDMODE_MOD) {
  5071. return SDL_BLENDMODE_MOD_FULL;
  5072. }
  5073. if (blendMode == SDL_BLENDMODE_MUL) {
  5074. return SDL_BLENDMODE_MUL_FULL;
  5075. }
  5076. return blendMode;
  5077. }
  5078. SDL_BlendMode SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor,
  5079. SDL_BlendOperation colorOperation,
  5080. SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor,
  5081. SDL_BlendOperation alphaOperation)
  5082. {
  5083. SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation,
  5084. srcAlphaFactor, dstAlphaFactor, alphaOperation);
  5085. return SDL_GetShortBlendMode(blendMode);
  5086. }
  5087. SDL_BlendFactor SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
  5088. {
  5089. blendMode = SDL_GetLongBlendMode(blendMode);
  5090. return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF);
  5091. }
  5092. SDL_BlendFactor SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
  5093. {
  5094. blendMode = SDL_GetLongBlendMode(blendMode);
  5095. return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF);
  5096. }
  5097. SDL_BlendOperation SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
  5098. {
  5099. blendMode = SDL_GetLongBlendMode(blendMode);
  5100. return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF);
  5101. }
  5102. SDL_BlendFactor SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
  5103. {
  5104. blendMode = SDL_GetLongBlendMode(blendMode);
  5105. return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF);
  5106. }
  5107. SDL_BlendFactor SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
  5108. {
  5109. blendMode = SDL_GetLongBlendMode(blendMode);
  5110. return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF);
  5111. }
  5112. SDL_BlendOperation SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
  5113. {
  5114. blendMode = SDL_GetLongBlendMode(blendMode);
  5115. return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF);
  5116. }
  5117. bool SDL_SetRenderVSync(SDL_Renderer *renderer, int vsync)
  5118. {
  5119. CHECK_RENDERER_MAGIC(renderer, false);
  5120. renderer->wanted_vsync = vsync ? true : false;
  5121. // for the software renderer, forward the call to the WindowTexture renderer
  5122. #ifdef SDL_VIDEO_RENDER_SW
  5123. if (renderer->software) {
  5124. if (!renderer->window) {
  5125. if (!vsync) {
  5126. return true;
  5127. } else {
  5128. return SDL_Unsupported();
  5129. }
  5130. }
  5131. if (SDL_SetWindowTextureVSync(NULL, renderer->window, vsync)) {
  5132. renderer->simulate_vsync = false;
  5133. return true;
  5134. }
  5135. }
  5136. #endif
  5137. if (!renderer->SetVSync ||
  5138. !renderer->SetVSync(renderer, vsync)) {
  5139. switch (vsync) {
  5140. case 0:
  5141. renderer->simulate_vsync = false;
  5142. break;
  5143. case 1:
  5144. renderer->simulate_vsync = true;
  5145. break;
  5146. default:
  5147. return SDL_Unsupported();
  5148. }
  5149. }
  5150. SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, vsync);
  5151. return true;
  5152. }
  5153. bool SDL_GetRenderVSync(SDL_Renderer *renderer, int *vsync)
  5154. {
  5155. if (vsync) {
  5156. *vsync = 0;
  5157. }
  5158. CHECK_RENDERER_MAGIC(renderer, false);
  5159. if (vsync) {
  5160. *vsync = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, 0);
  5161. }
  5162. return true;
  5163. }
  5164. #define SDL_DEBUG_FONT_GLYPHS_PER_ROW 14
  5165. static bool CreateDebugTextAtlas(SDL_Renderer *renderer)
  5166. {
  5167. static const SDL_Color colors[] = {
  5168. { 255, 255, 255, SDL_ALPHA_TRANSPARENT },
  5169. { 255, 255, 255, SDL_ALPHA_OPAQUE }
  5170. };
  5171. SDL_assert(renderer->debug_char_texture_atlas == NULL); // don't double-create it!
  5172. const int charWidth = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  5173. const int charHeight = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  5174. // actually make each glyph two pixels taller/wider, to prevent scaling artifacts.
  5175. const int rows = (SDL_DEBUG_FONT_NUM_GLYPHS / SDL_DEBUG_FONT_GLYPHS_PER_ROW) + 1;
  5176. SDL_Surface *atlas = SDL_CreateSurfaceZeroed((charWidth + 2) * SDL_DEBUG_FONT_GLYPHS_PER_ROW, rows * (charHeight + 2), SDL_PIXELFORMAT_INDEX8);
  5177. if (!atlas) {
  5178. return false;
  5179. }
  5180. SDL_Palette *palette = SDL_CreateSurfacePalette(atlas);
  5181. if (!palette || !SDL_SetPaletteColors(palette, colors, 0, 2)) {
  5182. SDL_DestroySurface(atlas);
  5183. return false;
  5184. }
  5185. const int pitch = atlas->pitch;
  5186. SDL_memset(atlas->pixels, '\0', atlas->h * atlas->pitch);
  5187. int column = 0;
  5188. int row = 0;
  5189. for (int glyph = 0; glyph < SDL_DEBUG_FONT_NUM_GLYPHS; glyph++) {
  5190. // find top-left of this glyph in destination surface. The +2's account for glyph padding.
  5191. Uint8 *linepos = (((Uint8 *)atlas->pixels) + ((row * (charHeight + 2) + 1) * pitch)) + ((column * (charWidth + 2) + 1) * sizeof (Uint8));
  5192. const Uint8 *charpos = SDL_RenderDebugTextFontData + (glyph * 8);
  5193. // Draw the glyph to the surface...
  5194. for (int iy = 0; iy < charHeight; iy++) {
  5195. Uint8 *curpos = linepos;
  5196. for (int ix = 0; ix < charWidth; ix++) {
  5197. *curpos++ = (*charpos >> ix) & 1;
  5198. }
  5199. linepos += pitch;
  5200. ++charpos;
  5201. }
  5202. // move to next position (and if too far, start the next row).
  5203. column++;
  5204. if (column >= SDL_DEBUG_FONT_GLYPHS_PER_ROW) {
  5205. row++;
  5206. column = 0;
  5207. }
  5208. }
  5209. SDL_assert((row < rows) || ((row == rows) && (column == 0))); // make sure we didn't overflow the surface.
  5210. // Convert temp surface into texture
  5211. SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, atlas);
  5212. if (texture) {
  5213. SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_PIXELART);
  5214. SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
  5215. renderer->debug_char_texture_atlas = texture;
  5216. }
  5217. SDL_DestroySurface(atlas);
  5218. return texture != NULL;
  5219. }
  5220. static bool DrawDebugCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c)
  5221. {
  5222. SDL_assert(renderer->debug_char_texture_atlas != NULL); // should have been created by now!
  5223. const int charWidth = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  5224. const int charHeight = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  5225. // Character index in cache
  5226. Uint32 ci = c;
  5227. if ((ci <= 32) || ((ci >= 127) && (ci <= 160))) {
  5228. return true; // these are just completely blank chars, don't bother doing anything.
  5229. } else if (ci >= SDL_DEBUG_FONT_NUM_GLYPHS) {
  5230. ci = SDL_DEBUG_FONT_NUM_GLYPHS - 1; // use our "not a valid/supported character" glyph.
  5231. } else if (ci < 127) {
  5232. ci -= 33; // adjust for the 33 blank glyphs at the start
  5233. } else {
  5234. ci -= 67; // adjust for the 33 blank glyphs at the start AND the 34 gap in the middle.
  5235. }
  5236. const float src_x = (float) (((ci % SDL_DEBUG_FONT_GLYPHS_PER_ROW) * (charWidth + 2)) + 1);
  5237. const float src_y = (float) (((ci / SDL_DEBUG_FONT_GLYPHS_PER_ROW) * (charHeight + 2)) + 1);
  5238. // Draw texture onto destination
  5239. const SDL_FRect srect = { src_x, src_y, (float) charWidth, (float) charHeight };
  5240. const SDL_FRect drect = { x, y, (float) charWidth, (float) charHeight };
  5241. return SDL_RenderTexture(renderer, renderer->debug_char_texture_atlas, &srect, &drect);
  5242. }
  5243. bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *s)
  5244. {
  5245. CHECK_RENDERER_MAGIC(renderer, false);
  5246. // Allocate a texture atlas for this renderer if needed.
  5247. if (!renderer->debug_char_texture_atlas) {
  5248. if (!CreateDebugTextAtlas(renderer)) {
  5249. return false;
  5250. }
  5251. }
  5252. bool result = true;
  5253. Uint8 r, g, b, a;
  5254. result &= SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a);
  5255. result &= SDL_SetTextureColorMod(renderer->debug_char_texture_atlas, r, g, b);
  5256. result &= SDL_SetTextureAlphaMod(renderer->debug_char_texture_atlas, a);
  5257. float curx = x;
  5258. Uint32 ch;
  5259. while (result && ((ch = SDL_StepUTF8(&s, NULL)) != 0)) {
  5260. result &= DrawDebugCharacter(renderer, curx, y, ch);
  5261. curx += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  5262. }
  5263. return result;
  5264. }
  5265. bool SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
  5266. {
  5267. va_list ap;
  5268. va_start(ap, fmt);
  5269. // fast path to avoid unnecessary allocation and copy. If you're going through the dynapi, there's a good chance
  5270. // you _always_ hit this path, since it probably had to process varargs before calling into the jumptable.
  5271. if (SDL_strcmp(fmt, "%s") == 0) {
  5272. const char *str = va_arg(ap, const char *);
  5273. va_end(ap);
  5274. return SDL_RenderDebugText(renderer, x, y, str);
  5275. }
  5276. char *str = NULL;
  5277. const int rc = SDL_vasprintf(&str, fmt, ap);
  5278. va_end(ap);
  5279. if (rc == -1) {
  5280. return false;
  5281. }
  5282. const bool retval = SDL_RenderDebugText(renderer, x, y, str);
  5283. SDL_free(str);
  5284. return retval;
  5285. }
  5286. bool SDL_SetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode scale_mode)
  5287. {
  5288. CHECK_RENDERER_MAGIC(renderer, false);
  5289. renderer->scale_mode = scale_mode;
  5290. return true;
  5291. }
  5292. bool SDL_GetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode *scale_mode)
  5293. {
  5294. if (scale_mode) {
  5295. *scale_mode = SDL_SCALEMODE_LINEAR;
  5296. }
  5297. CHECK_RENDERER_MAGIC(renderer, false);
  5298. if (scale_mode) {
  5299. *scale_mode = renderer->scale_mode;
  5300. }
  5301. return true;
  5302. }
  5303. SDL_GPURenderState *SDL_CreateGPURenderState(SDL_Renderer *renderer, const SDL_GPURenderStateCreateInfo *createinfo)
  5304. {
  5305. CHECK_RENDERER_MAGIC(renderer, NULL);
  5306. CHECK_PARAM(!createinfo) {
  5307. SDL_InvalidParamError("createinfo");
  5308. return NULL;
  5309. }
  5310. CHECK_PARAM(!createinfo->fragment_shader) {
  5311. SDL_SetError("A fragment_shader is required");
  5312. return NULL;
  5313. }
  5314. SDL_GPUDevice *device = (SDL_GPUDevice *)SDL_GetPointerProperty(renderer->props, SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);
  5315. if (!device) {
  5316. SDL_SetError("Renderer isn't associated with a GPU device");
  5317. return NULL;
  5318. }
  5319. SDL_GPURenderState *state = (SDL_GPURenderState *)SDL_calloc(1, sizeof(*state));
  5320. if (!state) {
  5321. return NULL;
  5322. }
  5323. state->renderer = renderer;
  5324. state->fragment_shader = createinfo->fragment_shader;
  5325. if (createinfo->num_sampler_bindings > 0) {
  5326. state->sampler_bindings = (SDL_GPUTextureSamplerBinding *)SDL_calloc(createinfo->num_sampler_bindings, sizeof(*state->sampler_bindings));
  5327. if (!state->sampler_bindings) {
  5328. SDL_DestroyGPURenderState(state);
  5329. return NULL;
  5330. }
  5331. SDL_memcpy(state->sampler_bindings, createinfo->sampler_bindings, createinfo->num_sampler_bindings * sizeof(*state->sampler_bindings));
  5332. state->num_sampler_bindings = createinfo->num_sampler_bindings;
  5333. }
  5334. if (createinfo->num_storage_textures > 0) {
  5335. state->storage_textures = (SDL_GPUTexture **)SDL_calloc(createinfo->num_storage_textures, sizeof(*state->storage_textures));
  5336. if (!state->storage_textures) {
  5337. SDL_DestroyGPURenderState(state);
  5338. return NULL;
  5339. }
  5340. SDL_memcpy(state->storage_textures, createinfo->storage_textures, createinfo->num_storage_textures * sizeof(*state->storage_textures));
  5341. state->num_storage_textures = createinfo->num_storage_textures;
  5342. }
  5343. if (createinfo->num_storage_buffers > 0) {
  5344. state->storage_buffers = (SDL_GPUBuffer **)SDL_calloc(createinfo->num_storage_buffers, sizeof(*state->storage_buffers));
  5345. if (!state->storage_buffers) {
  5346. SDL_DestroyGPURenderState(state);
  5347. return NULL;
  5348. }
  5349. SDL_memcpy(state->storage_buffers, createinfo->storage_buffers, createinfo->num_storage_buffers * sizeof(*state->storage_buffers));
  5350. state->num_storage_buffers = createinfo->num_storage_buffers;
  5351. }
  5352. return state;
  5353. }
  5354. bool SDL_SetGPURenderStateSamplerBindings(SDL_GPURenderState *state, int num_sampler_bindings, const SDL_GPUTextureSamplerBinding *sampler_bindings)
  5355. {
  5356. if (!state) {
  5357. return SDL_InvalidParamError("state");
  5358. }
  5359. if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) {
  5360. return false;
  5361. }
  5362. Sint32 length = sizeof(SDL_GPUTextureSamplerBinding) * num_sampler_bindings;
  5363. SDL_GPUTextureSamplerBinding *new_sampler_bindings = (SDL_GPUTextureSamplerBinding *)SDL_realloc(state->sampler_bindings, length);
  5364. if (!new_sampler_bindings) {
  5365. return false;
  5366. }
  5367. SDL_memcpy(new_sampler_bindings, sampler_bindings, length);
  5368. state->num_sampler_bindings = num_sampler_bindings;
  5369. state->sampler_bindings = new_sampler_bindings;
  5370. return true;
  5371. }
  5372. bool SDL_SetGPURenderStateStorageTextures(SDL_GPURenderState *state, int num_storage_textures, SDL_GPUTexture *const *storage_textures)
  5373. {
  5374. if (!state) {
  5375. return SDL_InvalidParamError("state");
  5376. }
  5377. if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) {
  5378. return false;
  5379. }
  5380. Sint32 length = sizeof(SDL_GPUTexture *) * num_storage_textures;
  5381. SDL_GPUTexture **new_storage_textures = (SDL_GPUTexture **)SDL_realloc(state->storage_textures, length);
  5382. if (!new_storage_textures) {
  5383. return false;
  5384. }
  5385. SDL_memcpy(new_storage_textures, storage_textures, length);
  5386. state->num_storage_textures = num_storage_textures;
  5387. state->storage_textures = new_storage_textures;
  5388. return true;
  5389. }
  5390. bool SDL_SetGPURenderStateStorageBuffers(SDL_GPURenderState *state, int num_storage_buffers, SDL_GPUBuffer *const *storage_buffers)
  5391. {
  5392. if (!state) {
  5393. return SDL_InvalidParamError("state");
  5394. }
  5395. if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) {
  5396. return false;
  5397. }
  5398. Sint32 length = sizeof(SDL_GPUBuffer *) * num_storage_buffers;
  5399. SDL_GPUBuffer **new_storage_buffers = (SDL_GPUBuffer **)SDL_realloc(state->storage_buffers, length);
  5400. if (!new_storage_buffers) {
  5401. return false;
  5402. }
  5403. SDL_memcpy(new_storage_buffers, storage_buffers, length);
  5404. state->num_storage_buffers = num_storage_buffers;
  5405. state->storage_buffers = new_storage_buffers;
  5406. return true;
  5407. }
  5408. bool SDL_SetGPURenderStateFragmentUniforms(SDL_GPURenderState *state, Uint32 slot_index, const void *data, Uint32 length)
  5409. {
  5410. if (!state) {
  5411. return SDL_InvalidParamError("state");
  5412. }
  5413. if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) {
  5414. return false;
  5415. }
  5416. for (int i = 0; i < state->num_uniform_buffers; i++) {
  5417. SDL_GPURenderStateUniformBuffer *buffer = &state->uniform_buffers[i];
  5418. if (buffer->slot_index == slot_index) {
  5419. void *new_data = SDL_realloc(buffer->data, length);
  5420. if (!new_data) {
  5421. return false;
  5422. }
  5423. SDL_memcpy(new_data, data, length);
  5424. buffer->data = new_data;
  5425. buffer->length = length;
  5426. return true;
  5427. }
  5428. }
  5429. SDL_GPURenderStateUniformBuffer *buffers = (SDL_GPURenderStateUniformBuffer *)SDL_realloc(state->uniform_buffers, (state->num_uniform_buffers + 1) * sizeof(*state->uniform_buffers));
  5430. if (!buffers) {
  5431. return false;
  5432. }
  5433. SDL_GPURenderStateUniformBuffer *buffer = &buffers[state->num_uniform_buffers];
  5434. buffer->slot_index = slot_index;
  5435. buffer->length = length;
  5436. buffer->data = SDL_malloc(length);
  5437. if (!buffer->data) {
  5438. SDL_free(buffers);
  5439. return false;
  5440. }
  5441. SDL_memcpy(buffer->data, data, length);
  5442. state->uniform_buffers = buffers;
  5443. ++state->num_uniform_buffers;
  5444. return true;
  5445. }
  5446. bool SDL_SetGPURenderState(SDL_Renderer *renderer, SDL_GPURenderState *state)
  5447. {
  5448. CHECK_RENDERER_MAGIC(renderer, false);
  5449. renderer->gpu_render_state = state;
  5450. return true;
  5451. }
  5452. void SDL_DestroyGPURenderState(SDL_GPURenderState *state)
  5453. {
  5454. if (!state) {
  5455. return;
  5456. }
  5457. FlushRenderCommandsIfGPURenderStateNeeded(state);
  5458. if (state->num_uniform_buffers > 0) {
  5459. for (int i = 0; i < state->num_uniform_buffers; i++) {
  5460. SDL_free(state->uniform_buffers[i].data);
  5461. }
  5462. SDL_free(state->uniform_buffers);
  5463. }
  5464. SDL_free(state->sampler_bindings);
  5465. SDL_free(state->storage_textures);
  5466. SDL_free(state->storage_buffers);
  5467. SDL_free(state);
  5468. }
  5469. #ifdef SDL_PLATFORM_GDK
  5470. void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer)
  5471. {
  5472. CHECK_RENDERER_MAGIC(renderer,);
  5473. if (renderer->GDKSuspendRenderer != NULL) {
  5474. renderer->GDKSuspendRenderer(renderer);
  5475. }
  5476. }
  5477. void SDLCALL SDL_GDKResumeRenderer(SDL_Renderer *renderer)
  5478. {
  5479. CHECK_RENDERER_MAGIC(renderer,);
  5480. if (renderer->GDKResumeRenderer != NULL) {
  5481. renderer->GDKResumeRenderer(renderer);
  5482. }
  5483. }
  5484. #endif /* SDL_PLATFORM_GDK */