1
0

image_test.go 105 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653
  1. // Copyright 2016 Hajime Hoshi
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ebiten_test
  15. import (
  16. "bytes"
  17. "fmt"
  18. "image"
  19. "image/color"
  20. "image/draw"
  21. _ "image/png"
  22. "math"
  23. "math/rand/v2"
  24. "runtime"
  25. "testing"
  26. "github.com/hajimehoshi/ebiten/v2"
  27. "github.com/hajimehoshi/ebiten/v2/examples/resources/images"
  28. "github.com/hajimehoshi/ebiten/v2/internal/graphics"
  29. t "github.com/hajimehoshi/ebiten/v2/internal/testing"
  30. "github.com/hajimehoshi/ebiten/v2/internal/ui"
  31. )
  32. // maxImageSize is a maximum image size that should work in almost every environment.
  33. const maxImageSize = 4096 - 2
  34. func skipTooSlowTests(t *testing.T) bool {
  35. if testing.Short() {
  36. t.Skip("skipping test in short mode")
  37. return true
  38. }
  39. if runtime.GOOS == "js" {
  40. t.Skip("too slow or fragile on Wasm")
  41. return true
  42. }
  43. return false
  44. }
  45. func TestMain(m *testing.M) {
  46. ui.SetPanicOnErrorOnReadingPixelsForTesting(true)
  47. t.MainWithRunLoop(m)
  48. }
  49. func openEbitenImage() (*ebiten.Image, image.Image, error) {
  50. img, _, err := image.Decode(bytes.NewReader(images.Ebiten_png))
  51. if err != nil {
  52. return nil, nil, err
  53. }
  54. eimg := ebiten.NewImageFromImage(img)
  55. return eimg, img, nil
  56. }
  57. func abs(x int) int {
  58. if x < 0 {
  59. return -x
  60. }
  61. return x
  62. }
  63. // sameColors compares c1 and c2 and returns a boolean value indicating
  64. // if the two colors are (almost) same.
  65. //
  66. // Pixels read from GPU might include errors (#492), and
  67. // sameColors considers such errors as delta.
  68. func sameColors(c1, c2 color.RGBA, delta int) bool {
  69. return abs(int(c1.R)-int(c2.R)) <= delta &&
  70. abs(int(c1.G)-int(c2.G)) <= delta &&
  71. abs(int(c1.B)-int(c2.B)) <= delta &&
  72. abs(int(c1.A)-int(c2.A)) <= delta
  73. }
  74. func TestImagePixels(t *testing.T) {
  75. img0, img, err := openEbitenImage()
  76. if err != nil {
  77. t.Fatal(err)
  78. return
  79. }
  80. if got := img0.Bounds().Size(); got != img.Bounds().Size() {
  81. t.Fatalf("img size: got %d; want %d", got, img.Bounds().Size())
  82. }
  83. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  84. // Check out of range part
  85. w2, h2 := graphics.InternalImageSize(w), graphics.InternalImageSize(h)
  86. for j := -100; j < h2+100; j++ {
  87. for i := -100; i < w2+100; i++ {
  88. got := img0.At(i, j)
  89. want := color.RGBAModel.Convert(img.At(i, j))
  90. if got != want {
  91. t.Errorf("img0 At(%d, %d): got %v; want %v", i, j, got, want)
  92. }
  93. }
  94. }
  95. pix := make([]byte, 4*w*h)
  96. img0.ReadPixels(pix)
  97. for j := 0; j < h; j++ {
  98. for i := 0; i < w; i++ {
  99. idx := 4 * (j*w + i)
  100. got := color.RGBA{R: pix[idx], G: pix[idx+1], B: pix[idx+2], A: pix[idx+3]}
  101. want := color.RGBAModel.Convert(img.At(i, j))
  102. if got != want {
  103. t.Errorf("(%d, %d): got %v; want %v", i, j, got, want)
  104. }
  105. }
  106. }
  107. }
  108. func TestImageComposition(t *testing.T) {
  109. img2Color := color.NRGBA{R: 0x24, G: 0x3f, B: 0x6a, A: 0x88}
  110. img3Color := color.NRGBA{R: 0x85, G: 0xa3, B: 0x08, A: 0xd3}
  111. // TODO: Rename this to img0
  112. img1, _, err := openEbitenImage()
  113. if err != nil {
  114. t.Fatal(err)
  115. return
  116. }
  117. w, h := img1.Bounds().Dx(), img1.Bounds().Dy()
  118. img2 := ebiten.NewImage(w, h)
  119. img3 := ebiten.NewImage(w, h)
  120. img2.Fill(img2Color)
  121. img3.Fill(img3Color)
  122. img_12_3 := ebiten.NewImage(w, h)
  123. img2.DrawImage(img1, nil)
  124. img3.DrawImage(img2, nil)
  125. img_12_3.DrawImage(img3, nil)
  126. img2.Fill(img2Color)
  127. img3.Fill(img3Color)
  128. img_1_23 := ebiten.NewImage(w, h)
  129. img3.DrawImage(img2, nil)
  130. img3.DrawImage(img1, nil)
  131. img_1_23.DrawImage(img3, nil)
  132. for j := 0; j < h; j++ {
  133. for i := 0; i < w; i++ {
  134. c1 := img_12_3.At(i, j).(color.RGBA)
  135. c2 := img_1_23.At(i, j).(color.RGBA)
  136. if !sameColors(c1, c2, 1) {
  137. t.Errorf("img_12_3.At(%d, %d) = %v; img_1_23.At(%[1]d, %[2]d) = %#[4]v", i, j, c1, c2)
  138. }
  139. if c1.A == 0 {
  140. t.Fatalf("img_12_3.At(%d, %d).A = 0; nothing is rendered?", i, j)
  141. }
  142. if c2.A == 0 {
  143. t.Fatalf("img_1_23.At(%d, %d).A = 0; nothing is rendered?", i, j)
  144. }
  145. }
  146. }
  147. }
  148. func TestImageSelf(t *testing.T) {
  149. // Note that mutex usages: without defer, unlocking is not called when panicking.
  150. defer func() {
  151. if r := recover(); r == nil {
  152. t.Errorf("DrawImage must panic but not")
  153. }
  154. }()
  155. img, _, err := openEbitenImage()
  156. if err != nil {
  157. t.Fatal(err)
  158. return
  159. }
  160. img.DrawImage(img, nil)
  161. }
  162. func TestImageScale(t *testing.T) {
  163. for _, scale := range []int{2, 3, 4} {
  164. img0, _, err := openEbitenImage()
  165. if err != nil {
  166. t.Fatal(err)
  167. return
  168. }
  169. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  170. img1 := ebiten.NewImage(w*scale, h*scale)
  171. op := &ebiten.DrawImageOptions{}
  172. op.GeoM.Scale(float64(scale), float64(scale))
  173. img1.DrawImage(img0, op)
  174. for j := 0; j < h*scale; j++ {
  175. for i := 0; i < w*scale; i++ {
  176. c0 := img0.At(i/scale, j/scale).(color.RGBA)
  177. c1 := img1.At(i, j).(color.RGBA)
  178. if c0 != c1 {
  179. t.Fatalf("img0.At(%[1]d, %[2]d) should equal to img1.At(%[3]d, %[4]d) (with scale %[5]d) but not: %[6]v vs %[7]v", i/2, j/2, i, j, scale, c0, c1)
  180. }
  181. }
  182. }
  183. }
  184. }
  185. func TestImage90DegreeRotate(t *testing.T) {
  186. img0, _, err := openEbitenImage()
  187. if err != nil {
  188. t.Fatal(err)
  189. return
  190. }
  191. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  192. img1 := ebiten.NewImage(h, w)
  193. op := &ebiten.DrawImageOptions{}
  194. op.GeoM.Rotate(math.Pi / 2)
  195. op.GeoM.Translate(float64(h), 0)
  196. img1.DrawImage(img0, op)
  197. for j := 0; j < h; j++ {
  198. for i := 0; i < w; i++ {
  199. c0 := img0.At(i, j).(color.RGBA)
  200. c1 := img1.At(h-j-1, i).(color.RGBA)
  201. if c0 != c1 {
  202. t.Errorf("img0.At(%[1]d, %[2]d) should equal to img1.At(%[3]d, %[4]d) but not: %[5]v vs %[6]v", i, j, h-j-1, i, c0, c1)
  203. }
  204. }
  205. }
  206. }
  207. func TestImageDotByDotInversion(t *testing.T) {
  208. img0, _, err := openEbitenImage()
  209. if err != nil {
  210. t.Fatal(err)
  211. return
  212. }
  213. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  214. img1 := ebiten.NewImage(w, h)
  215. op := &ebiten.DrawImageOptions{}
  216. op.GeoM.Rotate(math.Pi)
  217. op.GeoM.Translate(float64(w), float64(h))
  218. img1.DrawImage(img0, op)
  219. for j := 0; j < h; j++ {
  220. for i := 0; i < w; i++ {
  221. c0 := img0.At(i, j).(color.RGBA)
  222. c1 := img1.At(w-i-1, h-j-1).(color.RGBA)
  223. if c0 != c1 {
  224. t.Errorf("img0.At(%[1]d, %[2]d) should equal to img1.At(%[3]d, %[4]d) but not: %[5]v vs %[6]v", i, j, w-i-1, h-j-1, c0, c1)
  225. }
  226. }
  227. }
  228. }
  229. func TestImageWritePixels(t *testing.T) {
  230. // Create a dummy image so that the shared texture is used and origImg's position is shifted.
  231. dummyImg := ebiten.NewImageFromImage(image.NewRGBA(image.Rect(0, 0, 16, 16)))
  232. defer dummyImg.Deallocate()
  233. _, origImg, err := openEbitenImage()
  234. if err != nil {
  235. t.Fatal(err)
  236. return
  237. }
  238. // Convert to *image.RGBA just in case.
  239. img := image.NewRGBA(origImg.Bounds())
  240. draw.Draw(img, img.Bounds(), origImg, image.ZP, draw.Src)
  241. size := img.Bounds().Size()
  242. img0 := ebiten.NewImage(size.X, size.Y)
  243. img0.WritePixels(img.Pix)
  244. for j := 0; j < img0.Bounds().Dy(); j++ {
  245. for i := 0; i < img0.Bounds().Dx(); i++ {
  246. got := img0.At(i, j)
  247. want := img.At(i, j)
  248. if got != want {
  249. t.Errorf("img0 At(%d, %d): got %v; want %v", i, j, got, want)
  250. }
  251. }
  252. }
  253. p := make([]uint8, 4*size.X*size.Y)
  254. for i := range p {
  255. p[i] = 0x80
  256. }
  257. img0.WritePixels(p)
  258. // Even if p is changed after calling ReplacePixel, img0 uses the original values.
  259. for i := range p {
  260. p[i] = 0
  261. }
  262. for j := 0; j < img0.Bounds().Dy(); j++ {
  263. for i := 0; i < img0.Bounds().Dx(); i++ {
  264. got := img0.At(i, j)
  265. want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}
  266. if got != want {
  267. t.Errorf("img0 At(%d, %d): got %v; want %v", i, j, got, want)
  268. }
  269. }
  270. }
  271. }
  272. func TestImageWritePixelsNil(t *testing.T) {
  273. defer func() {
  274. if r := recover(); r == nil {
  275. t.Errorf("WritePixels(nil) must panic")
  276. }
  277. }()
  278. img := ebiten.NewImage(16, 16)
  279. img.Fill(color.White)
  280. img.WritePixels(nil)
  281. }
  282. func TestImageDispose(t *testing.T) {
  283. img := ebiten.NewImage(16, 16)
  284. img.Fill(color.White)
  285. img.Dispose()
  286. // The color is transparent (color.RGBA{}).
  287. // Note that the value's type must be color.RGBA.
  288. got := img.At(0, 0)
  289. want := color.RGBA{}
  290. if got != want {
  291. t.Errorf("img.At(0, 0) got: %v, want: %v", got, want)
  292. }
  293. }
  294. func TestImageDeallocate(t *testing.T) {
  295. img := ebiten.NewImage(16, 16)
  296. img.Fill(color.White)
  297. img.Deallocate()
  298. // The color is transparent (color.RGBA{}).
  299. got := img.At(0, 0)
  300. want := color.RGBA{}
  301. if got != want {
  302. t.Errorf("img.At(0, 0) got: %v, want: %v", got, want)
  303. }
  304. }
  305. func TestImageBlendLighter(t *testing.T) {
  306. img0, _, err := openEbitenImage()
  307. if err != nil {
  308. t.Fatal(err)
  309. return
  310. }
  311. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  312. img1 := ebiten.NewImage(w, h)
  313. img1.Fill(color.RGBA{R: 0x01, G: 0x02, B: 0x03, A: 0x04})
  314. op := &ebiten.DrawImageOptions{}
  315. op.Blend = ebiten.BlendLighter
  316. img1.DrawImage(img0, op)
  317. for j := 0; j < img1.Bounds().Dy(); j++ {
  318. for i := 0; i < img1.Bounds().Dx(); i++ {
  319. got := img1.At(i, j).(color.RGBA)
  320. want := img0.At(i, j).(color.RGBA)
  321. want.R = uint8(min(0xff, int(want.R)+1))
  322. want.G = uint8(min(0xff, int(want.G)+2))
  323. want.B = uint8(min(0xff, int(want.B)+3))
  324. want.A = uint8(min(0xff, int(want.A)+4))
  325. if got != want {
  326. t.Errorf("img1 At(%d, %d): got %v; want %v", i, j, got, want)
  327. }
  328. }
  329. }
  330. }
  331. func TestNewImageFromEbitenImage(t *testing.T) {
  332. img, _, err := openEbitenImage()
  333. if err != nil {
  334. t.Fatal(err)
  335. return
  336. }
  337. _ = ebiten.NewImageFromImage(img)
  338. }
  339. func TestNewImageFromSubImage(t *testing.T) {
  340. _, img, err := openEbitenImage()
  341. if err != nil {
  342. t.Fatal(err)
  343. return
  344. }
  345. w, h := img.Bounds().Dx(), img.Bounds().Dy()
  346. subImg := img.(*image.NRGBA).SubImage(image.Rect(1, 1, w-1, h-1))
  347. eimg := ebiten.NewImageFromImage(subImg)
  348. sw, sh := subImg.Bounds().Dx(), subImg.Bounds().Dy()
  349. w2, h2 := eimg.Bounds().Dx(), eimg.Bounds().Dy()
  350. if w2 != sw {
  351. t.Errorf("eimg Width: got %v; want %v", w2, sw)
  352. }
  353. if h2 != sh {
  354. t.Errorf("eimg Width: got %v; want %v", h2, sh)
  355. }
  356. for j := 0; j < h2; j++ {
  357. for i := 0; i < w2; i++ {
  358. got := eimg.At(i, j)
  359. want := color.RGBAModel.Convert(img.At(i+1, j+1))
  360. if got != want {
  361. t.Errorf("img0 At(%d, %d): got %v; want %v", i, j, got, want)
  362. }
  363. }
  364. }
  365. }
  366. type mutableRGBA struct {
  367. r, g, b, a uint8
  368. }
  369. func (c *mutableRGBA) RGBA() (r, g, b, a uint32) {
  370. return uint32(c.r) * 0x101, uint32(c.g) * 0x101, uint32(c.b) * 0x101, uint32(c.a) * 0x101
  371. }
  372. func TestImageFill(t *testing.T) {
  373. w, h := 10, 10
  374. img := ebiten.NewImage(w, h)
  375. clr := &mutableRGBA{0x80, 0x80, 0x80, 0x80}
  376. img.Fill(clr)
  377. clr.r = 0
  378. for j := 0; j < h; j++ {
  379. for i := 0; i < w; i++ {
  380. got := img.At(i, j)
  381. want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}
  382. if got != want {
  383. t.Errorf("img At(%d, %d): got %v; want %v", i, j, got, want)
  384. }
  385. }
  386. }
  387. }
  388. // Issue #740
  389. func TestImageClear(t *testing.T) {
  390. const w, h = 128, 256
  391. img := ebiten.NewImage(w, h)
  392. img.Fill(color.White)
  393. for j := 0; j < h; j++ {
  394. for i := 0; i < w; i++ {
  395. got := img.At(i, j)
  396. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  397. if got != want {
  398. t.Errorf("img At(%d, %d): got %v; want %v", i, j, got, want)
  399. }
  400. }
  401. }
  402. img.Clear()
  403. for j := 0; j < h; j++ {
  404. for i := 0; i < w; i++ {
  405. got := img.At(i, j)
  406. want := color.RGBA{}
  407. if got != want {
  408. t.Errorf("img At(%d, %d): got %v; want %v", i, j, got, want)
  409. }
  410. }
  411. }
  412. }
  413. // Issue #317, #558, #724
  414. func TestImageEdge(t *testing.T) {
  415. // TODO: This test is not so meaningful after #1218. Do we remove this?
  416. if skipTooSlowTests(t) {
  417. return
  418. }
  419. const (
  420. img0Width = 10
  421. img0Height = 10
  422. img0InnerWidth = 10
  423. img0InnerHeight = 10
  424. img1Width = 32
  425. img1Height = 32
  426. )
  427. img0 := ebiten.NewImage(img0Width, img0Height)
  428. pixels := make([]uint8, 4*img0Width*img0Height)
  429. for j := 0; j < img0Height; j++ {
  430. for i := 0; i < img0Width; i++ {
  431. idx := 4 * (i + j*img0Width)
  432. pixels[idx] = 0xff
  433. pixels[idx+1] = 0
  434. pixels[idx+2] = 0
  435. pixels[idx+3] = 0xff
  436. }
  437. }
  438. img0.WritePixels(pixels)
  439. img1 := ebiten.NewImage(img1Width, img1Height)
  440. red := color.RGBA{R: 0xff, A: 0xff}
  441. transparent := color.RGBA{}
  442. angles := []float64{}
  443. for a := 0; a < 1440; a++ {
  444. angles = append(angles, float64(a)/1440*2*math.Pi)
  445. }
  446. for a := 0; a < 4096; a += 3 {
  447. // a++ should be fine, but it takes long to test.
  448. angles = append(angles, float64(a)/4096*2*math.Pi)
  449. }
  450. for _, s := range []float64{1, 0.5, 0.25} {
  451. for _, f := range []ebiten.Filter{ebiten.FilterNearest, ebiten.FilterLinear} {
  452. for _, a := range angles {
  453. for _, testDrawTriangles := range []bool{false, true} {
  454. img1.Clear()
  455. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  456. b := img0.Bounds()
  457. var geo ebiten.GeoM
  458. geo.Translate(-float64(w)/2, -float64(h)/2)
  459. geo.Scale(s, s)
  460. geo.Rotate(a)
  461. geo.Translate(img1Width/2, img1Height/2)
  462. if !testDrawTriangles {
  463. op := &ebiten.DrawImageOptions{}
  464. op.GeoM = geo
  465. op.Filter = f
  466. img1.DrawImage(img0, op)
  467. } else {
  468. op := &ebiten.DrawTrianglesOptions{}
  469. dx0, dy0 := geo.Apply(0, 0)
  470. dx1, dy1 := geo.Apply(float64(w), 0)
  471. dx2, dy2 := geo.Apply(0, float64(h))
  472. dx3, dy3 := geo.Apply(float64(w), float64(h))
  473. vs := []ebiten.Vertex{
  474. {
  475. DstX: float32(dx0),
  476. DstY: float32(dy0),
  477. SrcX: float32(b.Min.X),
  478. SrcY: float32(b.Min.Y),
  479. ColorR: 1,
  480. ColorG: 1,
  481. ColorB: 1,
  482. ColorA: 1,
  483. },
  484. {
  485. DstX: float32(dx1),
  486. DstY: float32(dy1),
  487. SrcX: float32(b.Max.X),
  488. SrcY: float32(b.Min.Y),
  489. ColorR: 1,
  490. ColorG: 1,
  491. ColorB: 1,
  492. ColorA: 1,
  493. },
  494. {
  495. DstX: float32(dx2),
  496. DstY: float32(dy2),
  497. SrcX: float32(b.Min.X),
  498. SrcY: float32(b.Max.Y),
  499. ColorR: 1,
  500. ColorG: 1,
  501. ColorB: 1,
  502. ColorA: 1,
  503. },
  504. {
  505. DstX: float32(dx3),
  506. DstY: float32(dy3),
  507. SrcX: float32(b.Max.X),
  508. SrcY: float32(b.Max.Y),
  509. ColorR: 1,
  510. ColorG: 1,
  511. ColorB: 1,
  512. ColorA: 1,
  513. },
  514. }
  515. is := []uint16{0, 1, 2, 1, 2, 3}
  516. op.Filter = f
  517. img1.DrawTriangles(vs, is, img0, op)
  518. }
  519. allTransparent := true
  520. for j := 0; j < img1Height; j++ {
  521. for i := 0; i < img1Width; i++ {
  522. c := img1.At(i, j)
  523. if c == transparent {
  524. continue
  525. }
  526. allTransparent = false
  527. switch f {
  528. case ebiten.FilterNearest:
  529. if c == red {
  530. continue
  531. }
  532. case ebiten.FilterLinear:
  533. if _, g, b, _ := c.RGBA(); g == 0 && b == 0 {
  534. continue
  535. }
  536. }
  537. t.Fatalf("img1.At(%d, %d) (filter: %d, scale: %f, angle: %f, draw-triangles?: %t) want: red or transparent, got: %v", i, j, f, s, a, testDrawTriangles, c)
  538. }
  539. }
  540. if allTransparent {
  541. t.Fatalf("img1 (filter: %d, scale: %f, angle: %f, draw-triangles?: %t) is transparent but should not", f, s, a, testDrawTriangles)
  542. }
  543. }
  544. }
  545. }
  546. }
  547. }
  548. // Issue #419
  549. func TestImageTooManyFill(t *testing.T) {
  550. const width = 1024
  551. indexToColor := func(index int) uint8 {
  552. return uint8((17*index + 0x40) % 256)
  553. }
  554. src := ebiten.NewImage(1, 1)
  555. dst := ebiten.NewImage(width, 1)
  556. for i := 0; i < width; i++ {
  557. c := indexToColor(i)
  558. src.Fill(color.RGBA{R: c, G: c, B: c, A: 0xff})
  559. op := &ebiten.DrawImageOptions{}
  560. op.GeoM.Translate(float64(i), 0)
  561. dst.DrawImage(src, op)
  562. }
  563. for i := 0; i < width; i++ {
  564. c := indexToColor(i)
  565. got := dst.At(i, 0).(color.RGBA)
  566. want := color.RGBA{R: c, G: c, B: c, A: 0xff}
  567. if !sameColors(got, want, 1) {
  568. t.Errorf("dst.At(%d, %d): got %v, want: %v", i, 0, got, want)
  569. }
  570. }
  571. }
  572. func BenchmarkDrawImage(b *testing.B) {
  573. img0 := ebiten.NewImage(16, 16)
  574. img1 := ebiten.NewImage(16, 16)
  575. op := &ebiten.DrawImageOptions{}
  576. for i := 0; i < b.N; i++ {
  577. img0.DrawImage(img1, op)
  578. }
  579. }
  580. func BenchmarkDrawTriangles(b *testing.B) {
  581. const w, h = 16, 16
  582. img0 := ebiten.NewImage(w, h)
  583. img1 := ebiten.NewImage(w, h)
  584. op := &ebiten.DrawTrianglesOptions{}
  585. vs := []ebiten.Vertex{
  586. {
  587. DstX: 0,
  588. DstY: 0,
  589. SrcX: 0,
  590. SrcY: 0,
  591. ColorR: 1,
  592. ColorG: 1,
  593. ColorB: 1,
  594. ColorA: 1,
  595. },
  596. {
  597. DstX: w,
  598. DstY: 0,
  599. SrcX: w,
  600. SrcY: 0,
  601. ColorR: 1,
  602. ColorG: 1,
  603. ColorB: 1,
  604. ColorA: 1,
  605. },
  606. {
  607. DstX: 0,
  608. DstY: h,
  609. SrcX: 0,
  610. SrcY: h,
  611. ColorR: 1,
  612. ColorG: 1,
  613. ColorB: 1,
  614. ColorA: 1,
  615. },
  616. {
  617. DstX: w,
  618. DstY: h,
  619. SrcX: w,
  620. SrcY: h,
  621. ColorR: 1,
  622. ColorG: 1,
  623. ColorB: 1,
  624. ColorA: 1,
  625. },
  626. }
  627. is := []uint16{0, 1, 2, 1, 2, 3}
  628. for i := 0; i < b.N; i++ {
  629. img0.DrawTriangles(vs, is, img1, op)
  630. }
  631. }
  632. func TestImageLinearGraduation(t *testing.T) {
  633. img0 := ebiten.NewImage(2, 2)
  634. img0.WritePixels([]byte{
  635. 0xff, 0x00, 0x00, 0xff,
  636. 0x00, 0xff, 0x00, 0xff,
  637. 0x00, 0x00, 0xff, 0xff,
  638. 0xff, 0xff, 0xff, 0xff,
  639. })
  640. const w, h = 32, 32
  641. img1 := ebiten.NewImage(w, h)
  642. op := &ebiten.DrawImageOptions{}
  643. op.GeoM.Scale(w, h)
  644. op.GeoM.Translate(-w/4, -h/4)
  645. op.Filter = ebiten.FilterLinear
  646. img1.DrawImage(img0, op)
  647. for j := 1; j < h-1; j++ {
  648. for i := 1; i < w-1; i++ {
  649. c := img1.At(i, j).(color.RGBA)
  650. if c.R == 0 || c.R == 0xff {
  651. t.Errorf("img1.At(%d, %d).R must be in between 0x01 and 0xfe but %v", i, j, c)
  652. }
  653. }
  654. }
  655. }
  656. func TestImageOutside(t *testing.T) {
  657. src := ebiten.NewImage(5, 10) // internal texture size is 8x16.
  658. dst := ebiten.NewImage(4, 4)
  659. src.Fill(color.RGBA{R: 0xff, A: 0xff})
  660. cases := []struct {
  661. X, Y, Width, Height int
  662. }{
  663. {-4, -4, 4, 4},
  664. {5, 0, 4, 4},
  665. {0, 10, 4, 4},
  666. {5, 10, 4, 4},
  667. {8, 0, 4, 4},
  668. {0, 16, 4, 4},
  669. {8, 16, 4, 4},
  670. {8, -4, 4, 4},
  671. {-4, 16, 4, 4},
  672. {5, 10, 0, 0},
  673. {5, 10, -2, -2}, // non-well-formed rectangle
  674. }
  675. for _, c := range cases {
  676. dst.Clear()
  677. op := &ebiten.DrawImageOptions{}
  678. op.GeoM.Translate(0, 0)
  679. dst.DrawImage(src.SubImage(image.Rectangle{
  680. Min: image.Pt(c.X, c.Y),
  681. Max: image.Pt(c.X+c.Width, c.Y+c.Height),
  682. }).(*ebiten.Image), op)
  683. for j := 0; j < 4; j++ {
  684. for i := 0; i < 4; i++ {
  685. got := dst.At(i, j).(color.RGBA)
  686. want := color.RGBA{}
  687. if got != want {
  688. t.Errorf("src(x: %d, y: %d, w: %d, h: %d), dst At(%d, %d): got %v, want: %v", c.X, c.Y, c.Width, c.Height, i, j, got, want)
  689. }
  690. }
  691. }
  692. }
  693. }
  694. func TestImageOutsideUpperLeft(t *testing.T) {
  695. src := ebiten.NewImage(4, 4)
  696. dst1 := ebiten.NewImage(16, 16)
  697. dst2 := ebiten.NewImage(16, 16)
  698. src.Fill(color.RGBA{R: 0xff, A: 0xff})
  699. op := &ebiten.DrawImageOptions{}
  700. op.GeoM.Rotate(math.Pi / 4)
  701. dst1.DrawImage(src.SubImage(image.Rect(-4, -4, 8, 8)).(*ebiten.Image), op)
  702. op = &ebiten.DrawImageOptions{}
  703. op.GeoM.Rotate(math.Pi / 4)
  704. dst2.DrawImage(src, op)
  705. for j := 0; j < 16; j++ {
  706. for i := 0; i < 16; i++ {
  707. got := dst1.At(i, j).(color.RGBA)
  708. want := dst2.At(i, j).(color.RGBA)
  709. if got != want {
  710. t.Errorf("got: dst1.At(%d, %d): %v, want: dst2.At(%d, %d): %v", i, j, got, i, j, want)
  711. }
  712. }
  713. }
  714. }
  715. func TestImageSize(t *testing.T) {
  716. const (
  717. w = 17
  718. h = 31
  719. )
  720. img := ebiten.NewImage(w, h)
  721. gotW, gotH := img.Bounds().Dx(), img.Bounds().Dy()
  722. if gotW != w {
  723. t.Errorf("got: %d, want: %d", gotW, w)
  724. }
  725. if gotH != h {
  726. t.Errorf("got: %d, want: %d", gotH, h)
  727. }
  728. }
  729. func TestImageSize1(t *testing.T) {
  730. src := ebiten.NewImage(1, 1)
  731. dst := ebiten.NewImage(1, 1)
  732. src.Fill(color.White)
  733. dst.DrawImage(src, nil)
  734. got := src.At(0, 0).(color.RGBA)
  735. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  736. if !sameColors(got, want, 1) {
  737. t.Errorf("got: %v, want: %v", got, want)
  738. }
  739. }
  740. // TODO: Enable this test again. This test fails after #1217 is fixed.
  741. func Skip_TestImageSize4096(t *testing.T) {
  742. src := ebiten.NewImage(4096, 4096)
  743. dst := ebiten.NewImage(4096, 4096)
  744. pix := make([]byte, 4096*4096*4)
  745. for i := 0; i < 4096; i++ {
  746. j := 4095
  747. idx := 4 * (i + j*4096)
  748. pix[idx] = uint8(i + j)
  749. pix[idx+1] = uint8((i + j) >> 8)
  750. pix[idx+2] = uint8((i + j) >> 16)
  751. pix[idx+3] = 0xff
  752. }
  753. for j := 0; j < 4096; j++ {
  754. i := 4095
  755. idx := 4 * (i + j*4096)
  756. pix[idx] = uint8(i + j)
  757. pix[idx+1] = uint8((i + j) >> 8)
  758. pix[idx+2] = uint8((i + j) >> 16)
  759. pix[idx+3] = 0xff
  760. }
  761. src.WritePixels(pix)
  762. dst.DrawImage(src, nil)
  763. for i := 4095; i < 4096; i++ {
  764. j := 4095
  765. got := dst.At(i, j).(color.RGBA)
  766. want := color.RGBA{R: uint8(i + j), G: uint8((i + j) >> 8), B: uint8((i + j) >> 16), A: 0xff}
  767. if got != want {
  768. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  769. }
  770. }
  771. for j := 4095; j < 4096; j++ {
  772. i := 4095
  773. got := dst.At(i, j).(color.RGBA)
  774. want := color.RGBA{R: uint8(i + j), G: uint8((i + j) >> 8), B: uint8((i + j) >> 16), A: 0xff}
  775. if got != want {
  776. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  777. }
  778. }
  779. }
  780. func TestImageCopy(t *testing.T) {
  781. defer func() {
  782. if r := recover(); r == nil {
  783. t.Errorf("copying image and using it must panic")
  784. }
  785. }()
  786. img0 := ebiten.NewImage(256, 256)
  787. img1 := *img0
  788. img1.Fill(color.Transparent)
  789. }
  790. // Issue #611, #907
  791. func TestImageStretch(t *testing.T) {
  792. if skipTooSlowTests(t) {
  793. return
  794. }
  795. const w = 16
  796. dst := ebiten.NewImage(w, maxImageSize)
  797. loop:
  798. for h := 1; h <= 32; h++ {
  799. src := ebiten.NewImage(w+2, h+2)
  800. pix := make([]byte, 4*(w+2)*(h+2))
  801. for i := 0; i < (w+2)*(h+2); i++ {
  802. pix[4*i] = 0xff
  803. pix[4*i+3] = 0xff
  804. }
  805. src.WritePixels(pix)
  806. dh := dst.Bounds().Dy()
  807. for i := 0; i < dh; {
  808. dst.Clear()
  809. op := &ebiten.DrawImageOptions{}
  810. op.GeoM.Scale(1, float64(i)/float64(h))
  811. dst.DrawImage(src.SubImage(image.Rect(1, 1, w+1, h+1)).(*ebiten.Image), op)
  812. for j := -1; j <= 1; j++ {
  813. if i+j < 0 {
  814. continue
  815. }
  816. got := dst.At(0, i+j).(color.RGBA)
  817. want := color.RGBA{}
  818. if j < 0 {
  819. want = color.RGBA{R: 0xff, A: 0xff}
  820. }
  821. if got != want {
  822. t.Errorf("At(%d, %d) (height=%d, scale=%d/%d): got: %v, want: %v", 0, i+j, h, i, h, got, want)
  823. continue loop
  824. }
  825. }
  826. switch i % 32 {
  827. case 31, 0:
  828. i++
  829. case 1:
  830. i += 32 - 2
  831. default:
  832. panic("not reached")
  833. }
  834. }
  835. }
  836. }
  837. func TestImageSprites(t *testing.T) {
  838. const (
  839. width = 512
  840. height = 512
  841. )
  842. src := ebiten.NewImage(4, 4)
  843. src.Fill(color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
  844. dst := ebiten.NewImage(width, height)
  845. for j := 0; j < height/4; j++ {
  846. for i := 0; i < width/4; i++ {
  847. op := &ebiten.DrawImageOptions{}
  848. op.GeoM.Translate(float64(i*4), float64(j*4))
  849. dst.DrawImage(src, op)
  850. }
  851. }
  852. for j := 0; j < height/4; j++ {
  853. for i := 0; i < width/4; i++ {
  854. got := dst.At(i*4, j*4).(color.RGBA)
  855. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  856. if !sameColors(got, want, 1) {
  857. t.Errorf("dst.At(%d, %d): got %v, want: %v", i*4, j*4, got, want)
  858. }
  859. }
  860. }
  861. }
  862. // Disabled: it does not make sense to expect deterministic mipmap results (#909).
  863. func Disabled_TestImageMipmap(t *testing.T) {
  864. src, _, err := openEbitenImage()
  865. if err != nil {
  866. t.Fatal(err)
  867. return
  868. }
  869. w, h := src.Bounds().Dx(), src.Bounds().Dy()
  870. l1 := ebiten.NewImage(w/2, h/2)
  871. op := &ebiten.DrawImageOptions{}
  872. op.GeoM.Scale(1/2.0, 1/2.0)
  873. op.Filter = ebiten.FilterLinear
  874. l1.DrawImage(src, op)
  875. l1w, l1h := l1.Bounds().Dx(), l1.Bounds().Dy()
  876. l2 := ebiten.NewImage(l1w/2, l1h/2)
  877. op = &ebiten.DrawImageOptions{}
  878. op.GeoM.Scale(1/2.0, 1/2.0)
  879. op.Filter = ebiten.FilterLinear
  880. l2.DrawImage(l1, op)
  881. gotDst := ebiten.NewImage(w, h)
  882. op = &ebiten.DrawImageOptions{}
  883. op.GeoM.Scale(1/5.0, 1/5.0)
  884. op.Filter = ebiten.FilterLinear
  885. gotDst.DrawImage(src, op)
  886. wantDst := ebiten.NewImage(w, h)
  887. op = &ebiten.DrawImageOptions{}
  888. op.GeoM.Scale(4.0/5.0, 4.0/5.0)
  889. op.Filter = ebiten.FilterLinear
  890. wantDst.DrawImage(l2, op)
  891. for j := 0; j < h; j++ {
  892. for i := 0; i < w; i++ {
  893. got := gotDst.At(i, j).(color.RGBA)
  894. want := wantDst.At(i, j).(color.RGBA)
  895. if !sameColors(got, want, 1) {
  896. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  897. }
  898. }
  899. }
  900. }
  901. // Disabled: it does not make sense to expect deterministic mipmap results (#909).
  902. func Disabled_TestImageMipmapNegativeDet(t *testing.T) {
  903. src, _, err := openEbitenImage()
  904. if err != nil {
  905. t.Fatal(err)
  906. return
  907. }
  908. w, h := src.Bounds().Dx(), src.Bounds().Dy()
  909. l1 := ebiten.NewImage(w/2, h/2)
  910. op := &ebiten.DrawImageOptions{}
  911. op.GeoM.Scale(1/2.0, 1/2.0)
  912. op.Filter = ebiten.FilterLinear
  913. l1.DrawImage(src, op)
  914. l1w, l1h := l1.Bounds().Dx(), l1.Bounds().Dy()
  915. l2 := ebiten.NewImage(l1w/2, l1h/2)
  916. op = &ebiten.DrawImageOptions{}
  917. op.GeoM.Scale(1/2.0, 1/2.0)
  918. op.Filter = ebiten.FilterLinear
  919. l2.DrawImage(l1, op)
  920. gotDst := ebiten.NewImage(w, h)
  921. op = &ebiten.DrawImageOptions{}
  922. op.GeoM.Scale(-1/5.0, -1/5.0)
  923. op.GeoM.Translate(float64(w), float64(h))
  924. op.Filter = ebiten.FilterLinear
  925. gotDst.DrawImage(src, op)
  926. wantDst := ebiten.NewImage(w, h)
  927. op = &ebiten.DrawImageOptions{}
  928. op.GeoM.Scale(-4.0/5.0, -4.0/5.0)
  929. op.GeoM.Translate(float64(w), float64(h))
  930. op.Filter = ebiten.FilterLinear
  931. wantDst.DrawImage(l2, op)
  932. allZero := true
  933. for j := 0; j < h; j++ {
  934. for i := 0; i < w; i++ {
  935. got := gotDst.At(i, j).(color.RGBA)
  936. want := wantDst.At(i, j).(color.RGBA)
  937. if !sameColors(got, want, 1) {
  938. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  939. }
  940. if got.A > 0 {
  941. allZero = false
  942. }
  943. }
  944. }
  945. if allZero {
  946. t.Errorf("the image must include non-zero values but not")
  947. }
  948. }
  949. // Issue #710
  950. func TestImageMipmapColor(t *testing.T) {
  951. img0 := ebiten.NewImage(256, 256)
  952. img1 := ebiten.NewImage(128, 128)
  953. img1.Fill(color.White)
  954. for i := 0; i < 8; i++ {
  955. img0.Clear()
  956. s := 1 - float64(i)/8
  957. op := &ebiten.DrawImageOptions{}
  958. op.Filter = ebiten.FilterLinear
  959. op.GeoM.Scale(s, s)
  960. op.ColorScale.Scale(1, 1, 0, 1)
  961. img0.DrawImage(img1, op)
  962. op.GeoM.Translate(128, 0)
  963. op.ColorScale.Reset()
  964. op.ColorScale.Scale(0, 1, 1, 1)
  965. img0.DrawImage(img1, op)
  966. want := color.RGBA{G: 0xff, B: 0xff, A: 0xff}
  967. got := img0.At(128, 0)
  968. if got != want {
  969. t.Errorf("want: %v, got: %v", want, got)
  970. }
  971. }
  972. }
  973. // Issue #725
  974. func TestImageMiamapAndDrawTriangle(t *testing.T) {
  975. img0 := ebiten.NewImage(32, 32)
  976. img1 := ebiten.NewImage(128, 128)
  977. img2 := ebiten.NewImage(128, 128)
  978. // Fill img1 red and create img1's mipmap
  979. img1.Fill(color.RGBA{R: 0xff, A: 0xff})
  980. op := &ebiten.DrawImageOptions{}
  981. op.GeoM.Scale(0.25, 0.25)
  982. op.Filter = ebiten.FilterLinear
  983. img0.DrawImage(img1, op)
  984. // Call DrawTriangle on img1 and fill it with green
  985. img2.Fill(color.RGBA{G: 0xff, A: 0xff})
  986. vs := []ebiten.Vertex{
  987. {
  988. DstX: 0,
  989. DstY: 0,
  990. SrcX: 0,
  991. SrcY: 0,
  992. ColorR: 1,
  993. ColorG: 1,
  994. ColorB: 1,
  995. ColorA: 1,
  996. },
  997. {
  998. DstX: 128,
  999. DstY: 0,
  1000. SrcX: 128,
  1001. SrcY: 0,
  1002. ColorR: 1,
  1003. ColorG: 1,
  1004. ColorB: 1,
  1005. ColorA: 1,
  1006. },
  1007. {
  1008. DstX: 0,
  1009. DstY: 128,
  1010. SrcX: 0,
  1011. SrcY: 128,
  1012. ColorR: 1,
  1013. ColorG: 1,
  1014. ColorB: 1,
  1015. ColorA: 1,
  1016. },
  1017. {
  1018. DstX: 128,
  1019. DstY: 128,
  1020. SrcX: 128,
  1021. SrcY: 128,
  1022. ColorR: 1,
  1023. ColorG: 1,
  1024. ColorB: 1,
  1025. ColorA: 1,
  1026. },
  1027. }
  1028. img1.DrawTriangles(vs, []uint16{0, 1, 2, 1, 2, 3}, img2, nil)
  1029. // Draw img1 (green) again. Confirm mipmap is correctly updated.
  1030. img0.Clear()
  1031. op = &ebiten.DrawImageOptions{}
  1032. op.GeoM.Scale(0.25, 0.25)
  1033. op.Filter = ebiten.FilterLinear
  1034. img0.DrawImage(img1, op)
  1035. w, h := img0.Bounds().Dx(), img0.Bounds().Dy()
  1036. for j := 0; j < h; j++ {
  1037. for i := 0; i < w; i++ {
  1038. c := img0.At(i, j).(color.RGBA)
  1039. if c.R != 0 {
  1040. t.Errorf("img0.At(%d, %d): red want %d got %d", i, j, 0, c.R)
  1041. }
  1042. }
  1043. }
  1044. }
  1045. func TestImageSubImageAt(t *testing.T) {
  1046. img := ebiten.NewImage(16, 16)
  1047. img.Fill(color.RGBA{R: 0xff, A: 0xff})
  1048. got := img.SubImage(image.Rect(1, 1, 16, 16)).At(0, 0).(color.RGBA)
  1049. want := color.RGBA{}
  1050. if got != want {
  1051. t.Errorf("got: %v, want: %v", got, want)
  1052. }
  1053. got = img.SubImage(image.Rect(1, 1, 16, 16)).At(1, 1).(color.RGBA)
  1054. want = color.RGBA{R: 0xff, A: 0xff}
  1055. if got != want {
  1056. t.Errorf("got: %v, want: %v", got, want)
  1057. }
  1058. }
  1059. func TestImageSubImageSize(t *testing.T) {
  1060. img := ebiten.NewImage(16, 16)
  1061. img.Fill(color.RGBA{R: 0xff, A: 0xff})
  1062. got := img.SubImage(image.Rect(1, 1, 16, 16)).Bounds().Dx()
  1063. want := 15
  1064. if got != want {
  1065. t.Errorf("got: %v, want: %v", got, want)
  1066. }
  1067. }
  1068. func TestImageDrawImmediately(t *testing.T) {
  1069. const w, h = 16, 16
  1070. img0 := ebiten.NewImage(w, h)
  1071. img1 := ebiten.NewImage(w, h)
  1072. // Do not manipulate img0 here.
  1073. img0.Fill(color.RGBA{R: 0xff, A: 0xff})
  1074. for j := 0; j < h; j++ {
  1075. for i := 0; i < w; i++ {
  1076. got := img0.At(i, j).(color.RGBA)
  1077. want := color.RGBA{R: 0xff, A: 0xff}
  1078. if got != want {
  1079. t.Errorf("img0.At(%d, %d): got %v, want: %v", i, j, got, want)
  1080. }
  1081. }
  1082. }
  1083. img0.DrawImage(img1, nil)
  1084. for j := 0; j < h; j++ {
  1085. for i := 0; i < w; i++ {
  1086. got := img0.At(i, j).(color.RGBA)
  1087. want := color.RGBA{R: 0xff, A: 0xff}
  1088. if got != want {
  1089. t.Errorf("img0.At(%d, %d): got %v, want: %v", i, j, got, want)
  1090. }
  1091. }
  1092. }
  1093. }
  1094. // Issue #669, #759
  1095. func TestImageLinearFilterGlitch(t *testing.T) {
  1096. const w, h = 200, 12
  1097. const scale = 1.2
  1098. src := ebiten.NewImage(w, h)
  1099. dst := ebiten.NewImage(int(math.Floor(w*scale)), h)
  1100. pix := make([]byte, 4*w*h)
  1101. for j := 0; j < h; j++ {
  1102. for i := 0; i < w; i++ {
  1103. idx := i + w*j
  1104. if j < 3 {
  1105. pix[4*idx] = 0xff
  1106. pix[4*idx+1] = 0xff
  1107. pix[4*idx+2] = 0xff
  1108. pix[4*idx+3] = 0xff
  1109. } else {
  1110. pix[4*idx] = 0
  1111. pix[4*idx+1] = 0
  1112. pix[4*idx+2] = 0
  1113. pix[4*idx+3] = 0xff
  1114. }
  1115. }
  1116. }
  1117. src.WritePixels(pix)
  1118. for _, f := range []ebiten.Filter{ebiten.FilterNearest, ebiten.FilterLinear} {
  1119. op := &ebiten.DrawImageOptions{}
  1120. op.GeoM.Scale(scale, 1)
  1121. op.Filter = f
  1122. dst.DrawImage(src, op)
  1123. for j := 1; j < h-1; j++ {
  1124. offset := int(math.Ceil(scale))
  1125. for i := offset; i < int(math.Floor(w*scale))-offset; i++ {
  1126. got := dst.At(i, j).(color.RGBA)
  1127. var want color.RGBA
  1128. if j < 3 {
  1129. want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  1130. } else {
  1131. want = color.RGBA{A: 0xff}
  1132. }
  1133. if got != want {
  1134. t.Errorf("dst.At(%d, %d): filter: %d, got: %v, want: %v", i, j, f, got, want)
  1135. }
  1136. }
  1137. }
  1138. }
  1139. }
  1140. // Issue #1212
  1141. func TestImageLinearFilterGlitch2(t *testing.T) {
  1142. const w, h = 100, 100
  1143. src := ebiten.NewImage(w, h)
  1144. dst := ebiten.NewImage(w, h)
  1145. idx := 0
  1146. pix := make([]byte, 4*w*h)
  1147. for j := 0; j < h; j++ {
  1148. for i := 0; i < w; i++ {
  1149. if i+j < 100 {
  1150. pix[4*idx] = 0
  1151. pix[4*idx+1] = 0
  1152. pix[4*idx+2] = 0
  1153. pix[4*idx+3] = 0xff
  1154. } else {
  1155. pix[4*idx] = 0xff
  1156. pix[4*idx+1] = 0xff
  1157. pix[4*idx+2] = 0xff
  1158. pix[4*idx+3] = 0xff
  1159. }
  1160. idx++
  1161. }
  1162. }
  1163. src.WritePixels(pix)
  1164. op := &ebiten.DrawImageOptions{}
  1165. op.Filter = ebiten.FilterLinear
  1166. dst.DrawImage(src, op)
  1167. for j := 0; j < h; j++ {
  1168. for i := 0; i < w; i++ {
  1169. got := dst.At(i, j).(color.RGBA)
  1170. var want color.RGBA
  1171. if i+j < 100 {
  1172. want = color.RGBA{A: 0xff}
  1173. } else {
  1174. want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  1175. }
  1176. if !sameColors(got, want, 1) {
  1177. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  1178. }
  1179. }
  1180. }
  1181. }
  1182. func TestImageAddressRepeat(t *testing.T) {
  1183. const w, h = 16, 16
  1184. src := ebiten.NewImage(w, h)
  1185. dst := ebiten.NewImage(w, h)
  1186. pix := make([]byte, 4*w*h)
  1187. for j := 0; j < h; j++ {
  1188. for i := 0; i < w; i++ {
  1189. idx := 4 * (i + j*w)
  1190. if 4 <= i && i < 8 && 4 <= j && j < 8 {
  1191. pix[idx] = byte(i-4) * 0x10
  1192. pix[idx+1] = byte(j-4) * 0x10
  1193. pix[idx+2] = 0
  1194. pix[idx+3] = 0xff
  1195. } else {
  1196. pix[idx] = 0
  1197. pix[idx+1] = 0
  1198. pix[idx+2] = 0xff
  1199. pix[idx+3] = 0xff
  1200. }
  1201. }
  1202. }
  1203. src.WritePixels(pix)
  1204. vs := []ebiten.Vertex{
  1205. {
  1206. DstX: 0,
  1207. DstY: 0,
  1208. SrcX: 0,
  1209. SrcY: 0,
  1210. ColorR: 1,
  1211. ColorG: 1,
  1212. ColorB: 1,
  1213. ColorA: 1,
  1214. },
  1215. {
  1216. DstX: w,
  1217. DstY: 0,
  1218. SrcX: w,
  1219. SrcY: 0,
  1220. ColorR: 1,
  1221. ColorG: 1,
  1222. ColorB: 1,
  1223. ColorA: 1,
  1224. },
  1225. {
  1226. DstX: 0,
  1227. DstY: h,
  1228. SrcX: 0,
  1229. SrcY: h,
  1230. ColorR: 1,
  1231. ColorG: 1,
  1232. ColorB: 1,
  1233. ColorA: 1,
  1234. },
  1235. {
  1236. DstX: w,
  1237. DstY: h,
  1238. SrcX: w,
  1239. SrcY: h,
  1240. ColorR: 1,
  1241. ColorG: 1,
  1242. ColorB: 1,
  1243. ColorA: 1,
  1244. },
  1245. }
  1246. is := []uint16{0, 1, 2, 1, 2, 3}
  1247. op := &ebiten.DrawTrianglesOptions{}
  1248. op.Address = ebiten.AddressRepeat
  1249. dst.DrawTriangles(vs, is, src.SubImage(image.Rect(4, 4, 8, 8)).(*ebiten.Image), op)
  1250. for j := 0; j < h; j++ {
  1251. for i := 0; i < w; i++ {
  1252. got := dst.At(i, j).(color.RGBA)
  1253. want := color.RGBA{R: byte(i%4) * 0x10, G: byte(j%4) * 0x10, A: 0xff}
  1254. if !sameColors(got, want, 1) {
  1255. t.Errorf("dst.At(%d, %d): got %v, want: %v", i, j, got, want)
  1256. }
  1257. }
  1258. }
  1259. }
  1260. func TestImageAddressRepeatNegativePosition(t *testing.T) {
  1261. const w, h = 16, 16
  1262. src := ebiten.NewImage(w, h)
  1263. dst := ebiten.NewImage(w, h)
  1264. pix := make([]byte, 4*w*h)
  1265. for j := 0; j < h; j++ {
  1266. for i := 0; i < w; i++ {
  1267. idx := 4 * (i + j*w)
  1268. if 4 <= i && i < 8 && 4 <= j && j < 8 {
  1269. pix[idx] = byte(i-4) * 0x10
  1270. pix[idx+1] = byte(j-4) * 0x10
  1271. pix[idx+2] = 0
  1272. pix[idx+3] = 0xff
  1273. } else {
  1274. pix[idx] = 0
  1275. pix[idx+1] = 0
  1276. pix[idx+2] = 0xff
  1277. pix[idx+3] = 0xff
  1278. }
  1279. }
  1280. }
  1281. src.WritePixels(pix)
  1282. vs := []ebiten.Vertex{
  1283. {
  1284. DstX: 0,
  1285. DstY: 0,
  1286. SrcX: -w,
  1287. SrcY: -h,
  1288. ColorR: 1,
  1289. ColorG: 1,
  1290. ColorB: 1,
  1291. ColorA: 1,
  1292. },
  1293. {
  1294. DstX: w,
  1295. DstY: 0,
  1296. SrcX: 0,
  1297. SrcY: -h,
  1298. ColorR: 1,
  1299. ColorG: 1,
  1300. ColorB: 1,
  1301. ColorA: 1,
  1302. },
  1303. {
  1304. DstX: 0,
  1305. DstY: h,
  1306. SrcX: -w,
  1307. SrcY: 0,
  1308. ColorR: 1,
  1309. ColorG: 1,
  1310. ColorB: 1,
  1311. ColorA: 1,
  1312. },
  1313. {
  1314. DstX: w,
  1315. DstY: h,
  1316. SrcX: 0,
  1317. SrcY: 0,
  1318. ColorR: 1,
  1319. ColorG: 1,
  1320. ColorB: 1,
  1321. ColorA: 1,
  1322. },
  1323. }
  1324. is := []uint16{0, 1, 2, 1, 2, 3}
  1325. op := &ebiten.DrawTrianglesOptions{}
  1326. op.Address = ebiten.AddressRepeat
  1327. dst.DrawTriangles(vs, is, src.SubImage(image.Rect(4, 4, 8, 8)).(*ebiten.Image), op)
  1328. for j := 0; j < h; j++ {
  1329. for i := 0; i < w; i++ {
  1330. got := dst.At(i, j).(color.RGBA)
  1331. want := color.RGBA{R: byte(i%4) * 0x10, G: byte(j%4) * 0x10, A: 0xff}
  1332. if !sameColors(got, want, 1) {
  1333. t.Errorf("dst.At(%d, %d): got %v, want: %v", i, j, got, want)
  1334. }
  1335. }
  1336. }
  1337. }
  1338. func TestImageWritePixelsAfterClear(t *testing.T) {
  1339. const w, h = 256, 256
  1340. img := ebiten.NewImage(w, h)
  1341. img.WritePixels(make([]byte, 4*w*h))
  1342. // Clear used to call DrawImage to clear the image, which was the cause of crash. It is because after
  1343. // DrawImage is called, WritePixels for a region is forbidden.
  1344. //
  1345. // Now WritePixels was always called at Clear instead.
  1346. img.Clear()
  1347. img.WritePixels(make([]byte, 4*w*h))
  1348. // The test passes if this doesn't crash.
  1349. }
  1350. func TestImageSet(t *testing.T) {
  1351. type Pt struct {
  1352. X, Y int
  1353. }
  1354. const w, h = 16, 16
  1355. img := ebiten.NewImage(w, h)
  1356. colors := map[Pt]color.RGBA{
  1357. {1, 2}: {3, 4, 5, 6},
  1358. {7, 8}: {9, 10, 11, 12},
  1359. {13, 14}: {15, 16, 17, 18},
  1360. {-1, -1}: {19, 20, 21, 22},
  1361. }
  1362. for p, c := range colors {
  1363. img.Set(p.X, p.Y, c)
  1364. }
  1365. for j := 0; j < h; j++ {
  1366. for i := 0; i < w; i++ {
  1367. got := img.At(i, j).(color.RGBA)
  1368. var want color.RGBA
  1369. if c, ok := colors[Pt{i, j}]; ok {
  1370. want = c
  1371. }
  1372. if got != want {
  1373. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  1374. }
  1375. }
  1376. }
  1377. }
  1378. func TestImageSetAndDraw(t *testing.T) {
  1379. type Pt struct {
  1380. X, Y int
  1381. }
  1382. const w, h = 16, 16
  1383. src := ebiten.NewImage(w, h)
  1384. dst := ebiten.NewImage(w, h)
  1385. colors := map[Pt]color.RGBA{
  1386. {1, 2}: {3, 4, 5, 6},
  1387. {7, 8}: {9, 10, 11, 12},
  1388. {13, 14}: {15, 16, 17, 18},
  1389. }
  1390. for p, c := range colors {
  1391. src.Set(p.X, p.Y, c)
  1392. dst.Set(p.X+1, p.Y+1, c)
  1393. }
  1394. dst.DrawImage(src, nil)
  1395. for j := 0; j < h; j++ {
  1396. for i := 0; i < w; i++ {
  1397. got := dst.At(i, j).(color.RGBA)
  1398. var want color.RGBA
  1399. if c, ok := colors[Pt{i, j}]; ok {
  1400. want = c
  1401. }
  1402. if c, ok := colors[Pt{i - 1, j - 1}]; ok {
  1403. want = c
  1404. }
  1405. if got != want {
  1406. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  1407. }
  1408. }
  1409. }
  1410. src.Clear()
  1411. dst.Clear()
  1412. for p, c := range colors {
  1413. src.Set(p.X, p.Y, c)
  1414. dst.Set(p.X+1, p.Y+1, c)
  1415. }
  1416. op := &ebiten.DrawImageOptions{}
  1417. op.GeoM.Translate(2, 2)
  1418. dst.DrawImage(src.SubImage(image.Rect(2, 2, w-2, h-2)).(*ebiten.Image), op)
  1419. for j := 0; j < h; j++ {
  1420. for i := 0; i < w; i++ {
  1421. got := dst.At(i, j).(color.RGBA)
  1422. var want color.RGBA
  1423. if 2 <= i && 2 <= j && i < w-2 && j < h-2 {
  1424. if c, ok := colors[Pt{i, j}]; ok {
  1425. want = c
  1426. }
  1427. }
  1428. if c, ok := colors[Pt{i - 1, j - 1}]; ok {
  1429. want = c
  1430. }
  1431. if got != want {
  1432. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  1433. }
  1434. }
  1435. }
  1436. }
  1437. func TestImageAlphaOnBlack(t *testing.T) {
  1438. const w, h = 16, 16
  1439. src0 := ebiten.NewImage(w, h)
  1440. src1 := ebiten.NewImage(w, h)
  1441. dst0 := ebiten.NewImage(w, h)
  1442. dst1 := ebiten.NewImage(w, h)
  1443. pix0 := make([]byte, 4*w*h)
  1444. for j := 0; j < h; j++ {
  1445. for i := 0; i < w; i++ {
  1446. if (i/3)%2 == (j/3)%2 {
  1447. pix0[4*(i+j*w)] = 0xff
  1448. pix0[4*(i+j*w)+1] = 0xff
  1449. pix0[4*(i+j*w)+2] = 0xff
  1450. pix0[4*(i+j*w)+3] = 0xff
  1451. }
  1452. }
  1453. }
  1454. src0.WritePixels(pix0)
  1455. pix1 := make([]byte, 4*w*h)
  1456. for j := 0; j < h; j++ {
  1457. for i := 0; i < w; i++ {
  1458. if (i/3)%2 == (j/3)%2 {
  1459. pix1[4*(i+j*w)] = 0xff
  1460. pix1[4*(i+j*w)+1] = 0xff
  1461. pix1[4*(i+j*w)+2] = 0xff
  1462. pix1[4*(i+j*w)+3] = 0xff
  1463. } else {
  1464. pix1[4*(i+j*w)] = 0
  1465. pix1[4*(i+j*w)+1] = 0
  1466. pix1[4*(i+j*w)+2] = 0
  1467. pix1[4*(i+j*w)+3] = 0xff
  1468. }
  1469. }
  1470. }
  1471. src1.WritePixels(pix1)
  1472. dst0.Fill(color.Black)
  1473. dst1.Fill(color.Black)
  1474. op := &ebiten.DrawImageOptions{}
  1475. op.GeoM.Scale(0.5, 0.5)
  1476. op.Filter = ebiten.FilterLinear
  1477. dst0.DrawImage(src0, op)
  1478. dst1.DrawImage(src1, op)
  1479. gray := false
  1480. for j := 0; j < h; j++ {
  1481. for i := 0; i < w; i++ {
  1482. got := dst0.At(i, j)
  1483. want := dst1.At(i, j)
  1484. if got != want {
  1485. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  1486. }
  1487. if r := got.(color.RGBA).R; 0 < r && r < 255 {
  1488. gray = true
  1489. }
  1490. }
  1491. }
  1492. if !gray {
  1493. t.Errorf("gray must be included in the results but not")
  1494. }
  1495. }
  1496. func TestImageDrawTrianglesWithSubImage(t *testing.T) {
  1497. const w, h = 16, 16
  1498. src := ebiten.NewImage(w, h)
  1499. dst := ebiten.NewImage(w, h)
  1500. pix := make([]byte, 4*w*h)
  1501. for j := 0; j < h; j++ {
  1502. for i := 0; i < w; i++ {
  1503. if 4 <= i && i < 8 && 4 <= j && j < 8 {
  1504. pix[4*(i+j*w)] = 0xff
  1505. pix[4*(i+j*w)+1] = 0
  1506. pix[4*(i+j*w)+2] = 0
  1507. pix[4*(i+j*w)+3] = 0xff
  1508. } else {
  1509. pix[4*(i+j*w)] = 0
  1510. pix[4*(i+j*w)+1] = 0xff
  1511. pix[4*(i+j*w)+2] = 0
  1512. pix[4*(i+j*w)+3] = 0xff
  1513. }
  1514. }
  1515. }
  1516. src.WritePixels(pix)
  1517. vs := []ebiten.Vertex{
  1518. {
  1519. DstX: 0,
  1520. DstY: 0,
  1521. SrcX: 0,
  1522. SrcY: 0,
  1523. ColorR: 1,
  1524. ColorG: 1,
  1525. ColorB: 1,
  1526. ColorA: 1,
  1527. },
  1528. {
  1529. DstX: w,
  1530. DstY: 0,
  1531. SrcX: w,
  1532. SrcY: 0,
  1533. ColorR: 1,
  1534. ColorG: 1,
  1535. ColorB: 1,
  1536. ColorA: 1,
  1537. },
  1538. {
  1539. DstX: 0,
  1540. DstY: h,
  1541. SrcX: 0,
  1542. SrcY: h,
  1543. ColorR: 1,
  1544. ColorG: 1,
  1545. ColorB: 1,
  1546. ColorA: 1,
  1547. },
  1548. {
  1549. DstX: w,
  1550. DstY: h,
  1551. SrcX: w,
  1552. SrcY: h,
  1553. ColorR: 1,
  1554. ColorG: 1,
  1555. ColorB: 1,
  1556. ColorA: 1,
  1557. },
  1558. }
  1559. is := []uint16{0, 1, 2, 1, 2, 3}
  1560. op := &ebiten.DrawTrianglesOptions{}
  1561. op.Address = ebiten.AddressClampToZero
  1562. dst.DrawTriangles(vs, is, src.SubImage(image.Rect(4, 4, 8, 8)).(*ebiten.Image), op)
  1563. for j := 0; j < h; j++ {
  1564. for i := 0; i < w; i++ {
  1565. got := dst.At(i, j).(color.RGBA)
  1566. var want color.RGBA
  1567. if 4 <= i && i < 8 && 4 <= j && j < 8 {
  1568. want = src.At(i, j).(color.RGBA)
  1569. }
  1570. if !sameColors(got, want, 1) {
  1571. t.Errorf("dst.At(%d, %d): got %v, want: %v", i, j, got, want)
  1572. }
  1573. }
  1574. }
  1575. }
  1576. // Issue #823
  1577. func TestImageAtAfterDisposingSubImage(t *testing.T) {
  1578. img := ebiten.NewImage(16, 16)
  1579. img.Set(0, 0, color.White)
  1580. img.SubImage(image.Rect(0, 0, 16, 16))
  1581. runtime.GC()
  1582. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  1583. want64 := color.RGBA64{R: 0xffff, G: 0xffff, B: 0xffff, A: 0xffff}
  1584. got := img.At(0, 0)
  1585. if got != want {
  1586. t.Errorf("At(0,0) got: %v, want: %v", got, want)
  1587. }
  1588. got = img.RGBA64At(0, 0)
  1589. if got != want64 {
  1590. t.Errorf("RGBA64At(0,0) got: %v, want: %v", got, want)
  1591. }
  1592. img.Set(0, 1, color.White)
  1593. sub := img.SubImage(image.Rect(0, 0, 16, 16)).(*ebiten.Image)
  1594. sub.Dispose()
  1595. got = img.At(0, 1)
  1596. if got != want {
  1597. t.Errorf("At(0,1) got: %v, want: %v", got, want64)
  1598. }
  1599. got = img.RGBA64At(0, 1)
  1600. if got != want64 {
  1601. t.Errorf("RGBA64At(0,1) got: %v, want: %v", got, want64)
  1602. }
  1603. }
  1604. func TestImageAtAfterDeallocateSubImage(t *testing.T) {
  1605. img := ebiten.NewImage(16, 16)
  1606. img.Set(0, 0, color.White)
  1607. img.SubImage(image.Rect(0, 0, 16, 16))
  1608. runtime.GC()
  1609. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  1610. want64 := color.RGBA64{R: 0xffff, G: 0xffff, B: 0xffff, A: 0xffff}
  1611. got := img.At(0, 0)
  1612. if got != want {
  1613. t.Errorf("At(0,0) got: %v, want: %v", got, want)
  1614. }
  1615. got = img.RGBA64At(0, 0)
  1616. if got != want64 {
  1617. t.Errorf("RGBA64At(0,0) got: %v, want: %v", got, want)
  1618. }
  1619. img.Set(0, 1, color.White)
  1620. sub := img.SubImage(image.Rect(0, 0, 16, 16)).(*ebiten.Image)
  1621. sub.Deallocate()
  1622. got = img.At(0, 1)
  1623. if got != want {
  1624. t.Errorf("At(0,1) got: %v, want: %v", got, want64)
  1625. }
  1626. got = img.RGBA64At(0, 1)
  1627. if got != want64 {
  1628. t.Errorf("RGBA64At(0,1) got: %v, want: %v", got, want64)
  1629. }
  1630. }
  1631. func TestImageSubImageSubImage(t *testing.T) {
  1632. img := ebiten.NewImage(16, 16)
  1633. img.Fill(color.White)
  1634. sub0 := img.SubImage(image.Rect(0, 0, 12, 12)).(*ebiten.Image)
  1635. sub1 := sub0.SubImage(image.Rect(4, 4, 16, 16)).(*ebiten.Image)
  1636. cases := []struct {
  1637. X int
  1638. Y int
  1639. Color color.RGBA
  1640. }{
  1641. {
  1642. X: 0,
  1643. Y: 0,
  1644. Color: color.RGBA{},
  1645. },
  1646. {
  1647. X: 4,
  1648. Y: 4,
  1649. Color: color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff},
  1650. },
  1651. {
  1652. X: 15,
  1653. Y: 15,
  1654. Color: color.RGBA{},
  1655. },
  1656. }
  1657. for _, c := range cases {
  1658. got := sub1.At(c.X, c.Y)
  1659. want := c.Color
  1660. if got != want {
  1661. t.Errorf("At(%d, %d): got: %v, want: %v", c.X, c.Y, got, want)
  1662. }
  1663. }
  1664. }
  1665. // Issue #839
  1666. func TestImageTooSmallMipmap(t *testing.T) {
  1667. const w, h = 16, 16
  1668. src := ebiten.NewImage(w, h)
  1669. dst := ebiten.NewImage(w, h)
  1670. src.Fill(color.White)
  1671. op := &ebiten.DrawImageOptions{}
  1672. op.GeoM.Scale(1, 0.24)
  1673. op.Filter = ebiten.FilterLinear
  1674. dst.DrawImage(src.SubImage(image.Rect(5, 0, 6, 16)).(*ebiten.Image), op)
  1675. got := dst.At(0, 0).(color.RGBA)
  1676. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  1677. if got != want {
  1678. t.Errorf("got: %v, want: %v", got, want)
  1679. }
  1680. }
  1681. func TestImageZeroSizedMipmap(t *testing.T) {
  1682. const w, h = 16, 16
  1683. src := ebiten.NewImage(w, h)
  1684. dst := ebiten.NewImage(w, h)
  1685. op := &ebiten.DrawImageOptions{}
  1686. op.Filter = ebiten.FilterLinear
  1687. dst.DrawImage(src.SubImage(image.ZR).(*ebiten.Image), op)
  1688. }
  1689. // Issue #898
  1690. func TestImageFillingAndEdges(t *testing.T) {
  1691. const (
  1692. srcw, srch = 16, 16
  1693. dstw, dsth = 256, 16
  1694. )
  1695. src := ebiten.NewImage(srcw, srch)
  1696. dst := ebiten.NewImage(dstw, dsth)
  1697. src.Fill(color.White)
  1698. dst.Fill(color.Black)
  1699. op := &ebiten.DrawImageOptions{}
  1700. op.GeoM.Scale(float64(dstw-2)/float64(srcw), float64(dsth-2)/float64(srch))
  1701. op.GeoM.Translate(1, 1)
  1702. dst.DrawImage(src, op)
  1703. for j := 0; j < dsth; j++ {
  1704. for i := 0; i < dstw; i++ {
  1705. got := dst.At(i, j).(color.RGBA)
  1706. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  1707. if i == 0 || i == dstw-1 || j == 0 || j == dsth-1 {
  1708. want = color.RGBA{A: 0xff}
  1709. }
  1710. if got != want {
  1711. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  1712. }
  1713. }
  1714. }
  1715. }
  1716. func TestImageDrawTrianglesAndMutateArgs(t *testing.T) {
  1717. const w, h = 16, 16
  1718. dst := ebiten.NewImage(w, h)
  1719. src := ebiten.NewImage(w, h)
  1720. clr := color.RGBA{R: 0xff, A: 0xff}
  1721. src.Fill(clr)
  1722. vs := []ebiten.Vertex{
  1723. {
  1724. DstX: 0,
  1725. DstY: 0,
  1726. SrcX: 0,
  1727. SrcY: 0,
  1728. ColorR: 1,
  1729. ColorG: 1,
  1730. ColorB: 1,
  1731. ColorA: 1,
  1732. },
  1733. {
  1734. DstX: w,
  1735. DstY: 0,
  1736. SrcX: w,
  1737. SrcY: 0,
  1738. ColorR: 1,
  1739. ColorG: 1,
  1740. ColorB: 1,
  1741. ColorA: 1,
  1742. },
  1743. {
  1744. DstX: 0,
  1745. DstY: h,
  1746. SrcX: 0,
  1747. SrcY: h,
  1748. ColorR: 1,
  1749. ColorG: 1,
  1750. ColorB: 1,
  1751. ColorA: 1,
  1752. },
  1753. {
  1754. DstX: w,
  1755. DstY: h,
  1756. SrcX: w,
  1757. SrcY: h,
  1758. ColorR: 1,
  1759. ColorG: 1,
  1760. ColorB: 1,
  1761. ColorA: 1,
  1762. },
  1763. }
  1764. is := []uint16{0, 1, 2, 1, 2, 3}
  1765. dst.DrawTriangles(vs, is, src, nil)
  1766. vs[0].SrcX = w
  1767. vs[0].SrcY = h
  1768. is[5] = 0
  1769. for j := 0; j < w; j++ {
  1770. for i := 0; i < w; i++ {
  1771. got := dst.At(i, j)
  1772. want := clr
  1773. if got != want {
  1774. t.Errorf("dst.At(%d, %d): got %v, want %v", i, j, got, want)
  1775. }
  1776. }
  1777. }
  1778. }
  1779. func TestImageWritePixelsOnSubImage(t *testing.T) {
  1780. dst := ebiten.NewImage(17, 31)
  1781. dst.Fill(color.RGBA{R: 0xff, A: 0xff})
  1782. pix0 := make([]byte, 4*5*3)
  1783. idx := 0
  1784. for j := 0; j < 3; j++ {
  1785. for i := 0; i < 5; i++ {
  1786. pix0[4*idx] = 0
  1787. pix0[4*idx+1] = 0xff
  1788. pix0[4*idx+2] = 0
  1789. pix0[4*idx+3] = 0xff
  1790. idx++
  1791. }
  1792. }
  1793. r0 := image.Rect(4, 5, 9, 8)
  1794. dst.SubImage(r0).(*ebiten.Image).WritePixels(pix0)
  1795. pix1 := make([]byte, 4*5*3)
  1796. idx = 0
  1797. for j := 0; j < 3; j++ {
  1798. for i := 0; i < 5; i++ {
  1799. pix1[4*idx] = 0
  1800. pix1[4*idx+1] = 0
  1801. pix1[4*idx+2] = 0xff
  1802. pix1[4*idx+3] = 0xff
  1803. idx++
  1804. }
  1805. }
  1806. r1 := image.Rect(11, 10, 16, 13)
  1807. dst.SubImage(r1).(*ebiten.Image).WritePixels(pix1)
  1808. // Clear the pixels. This should not affect the result.
  1809. idx = 0
  1810. for j := 0; j < 3; j++ {
  1811. for i := 0; i < 5; i++ {
  1812. pix1[4*idx] = 0
  1813. pix1[4*idx+1] = 0
  1814. pix1[4*idx+2] = 0
  1815. pix1[4*idx+3] = 0
  1816. idx++
  1817. }
  1818. }
  1819. for j := 0; j < 31; j++ {
  1820. for i := 0; i < 17; i++ {
  1821. got := dst.At(i, j).(color.RGBA)
  1822. want := color.RGBA{R: 0xff, A: 0xff}
  1823. p := image.Pt(i, j)
  1824. switch {
  1825. case p.In(r0):
  1826. want = color.RGBA{G: 0xff, A: 0xff}
  1827. case p.In(r1):
  1828. want = color.RGBA{B: 0xff, A: 0xff}
  1829. }
  1830. if got != want {
  1831. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  1832. }
  1833. }
  1834. }
  1835. }
  1836. func TestImageDrawTrianglesWithColorM(t *testing.T) {
  1837. const w, h = 16, 16
  1838. dst0 := ebiten.NewImage(w, h)
  1839. src := ebiten.NewImage(w, h)
  1840. src.Fill(color.White)
  1841. vs0 := []ebiten.Vertex{
  1842. {
  1843. DstX: 0,
  1844. DstY: 0,
  1845. SrcX: 0,
  1846. SrcY: 0,
  1847. ColorR: 1,
  1848. ColorG: 1,
  1849. ColorB: 1,
  1850. ColorA: 1,
  1851. },
  1852. {
  1853. DstX: w,
  1854. DstY: 0,
  1855. SrcX: w,
  1856. SrcY: 0,
  1857. ColorR: 1,
  1858. ColorG: 1,
  1859. ColorB: 1,
  1860. ColorA: 1,
  1861. },
  1862. {
  1863. DstX: 0,
  1864. DstY: h,
  1865. SrcX: 0,
  1866. SrcY: h,
  1867. ColorR: 1,
  1868. ColorG: 1,
  1869. ColorB: 1,
  1870. ColorA: 1,
  1871. },
  1872. {
  1873. DstX: w,
  1874. DstY: h,
  1875. SrcX: w,
  1876. SrcY: h,
  1877. ColorR: 1,
  1878. ColorG: 1,
  1879. ColorB: 1,
  1880. ColorA: 1,
  1881. },
  1882. }
  1883. op := &ebiten.DrawTrianglesOptions{}
  1884. op.ColorM.Scale(0.2, 0.4, 0.6, 0.8)
  1885. is := []uint16{0, 1, 2, 1, 2, 3}
  1886. dst0.DrawTriangles(vs0, is, src, op)
  1887. for _, format := range []ebiten.ColorScaleMode{
  1888. ebiten.ColorScaleModeStraightAlpha,
  1889. ebiten.ColorScaleModePremultipliedAlpha,
  1890. } {
  1891. format := format
  1892. t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
  1893. var cr, cg, cb, ca float32
  1894. switch format {
  1895. case ebiten.ColorScaleModeStraightAlpha:
  1896. // The values are the same as ColorM.Scale
  1897. cr = 0.2
  1898. cg = 0.4
  1899. cb = 0.6
  1900. ca = 0.8
  1901. case ebiten.ColorScaleModePremultipliedAlpha:
  1902. cr = 0.2 * 0.8
  1903. cg = 0.4 * 0.8
  1904. cb = 0.6 * 0.8
  1905. ca = 0.8
  1906. }
  1907. vs1 := []ebiten.Vertex{
  1908. {
  1909. DstX: 0,
  1910. DstY: 0,
  1911. SrcX: 0,
  1912. SrcY: 0,
  1913. ColorR: cr,
  1914. ColorG: cg,
  1915. ColorB: cb,
  1916. ColorA: ca,
  1917. },
  1918. {
  1919. DstX: w,
  1920. DstY: 0,
  1921. SrcX: w,
  1922. SrcY: 0,
  1923. ColorR: cr,
  1924. ColorG: cg,
  1925. ColorB: cb,
  1926. ColorA: ca,
  1927. },
  1928. {
  1929. DstX: 0,
  1930. DstY: h,
  1931. SrcX: 0,
  1932. SrcY: h,
  1933. ColorR: cr,
  1934. ColorG: cg,
  1935. ColorB: cb,
  1936. ColorA: ca,
  1937. },
  1938. {
  1939. DstX: w,
  1940. DstY: h,
  1941. SrcX: w,
  1942. SrcY: h,
  1943. ColorR: cr,
  1944. ColorG: cg,
  1945. ColorB: cb,
  1946. ColorA: ca,
  1947. },
  1948. }
  1949. dst1 := ebiten.NewImage(w, h)
  1950. op := &ebiten.DrawTrianglesOptions{}
  1951. op.ColorScaleMode = format
  1952. dst1.DrawTriangles(vs1, is, src, op)
  1953. for j := 0; j < h; j++ {
  1954. for i := 0; i < w; i++ {
  1955. got := dst0.At(i, j)
  1956. want := dst1.At(i, j)
  1957. if got != want {
  1958. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  1959. }
  1960. }
  1961. }
  1962. })
  1963. }
  1964. }
  1965. func TestImageDrawTrianglesInterpolatesColors(t *testing.T) {
  1966. const w, h = 3, 1
  1967. src := ebiten.NewImage(w, h)
  1968. src.Fill(color.White)
  1969. vs := []ebiten.Vertex{
  1970. {
  1971. DstX: 0,
  1972. DstY: 0,
  1973. SrcX: 0,
  1974. SrcY: 0,
  1975. ColorR: 1,
  1976. ColorG: 0,
  1977. ColorB: 0,
  1978. ColorA: 0,
  1979. },
  1980. {
  1981. DstX: w,
  1982. DstY: 0,
  1983. SrcX: w,
  1984. SrcY: 0,
  1985. ColorR: 0,
  1986. ColorG: 1,
  1987. ColorB: 0,
  1988. ColorA: 1,
  1989. },
  1990. {
  1991. DstX: 0,
  1992. DstY: h,
  1993. SrcX: 0,
  1994. SrcY: h,
  1995. ColorR: 1,
  1996. ColorG: 0,
  1997. ColorB: 0,
  1998. ColorA: 0,
  1999. },
  2000. {
  2001. DstX: w,
  2002. DstY: h,
  2003. SrcX: w,
  2004. SrcY: h,
  2005. ColorR: 0,
  2006. ColorG: 1,
  2007. ColorB: 0,
  2008. ColorA: 1,
  2009. },
  2010. }
  2011. for _, format := range []ebiten.ColorScaleMode{
  2012. ebiten.ColorScaleModeStraightAlpha,
  2013. ebiten.ColorScaleModePremultipliedAlpha,
  2014. } {
  2015. format := format
  2016. t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
  2017. dst := ebiten.NewImage(w, h)
  2018. dst.Fill(color.RGBA{B: 0xff, A: 0xff})
  2019. op := &ebiten.DrawTrianglesOptions{}
  2020. op.ColorScaleMode = format
  2021. is := []uint16{0, 1, 2, 1, 2, 3}
  2022. dst.DrawTriangles(vs, is, src, op)
  2023. got := dst.At(1, 0).(color.RGBA)
  2024. // Correct color interpolation uses the alpha channel
  2025. // and notices that colors on the left side of the texture are fully transparent.
  2026. var want color.RGBA
  2027. switch format {
  2028. case ebiten.ColorScaleModeStraightAlpha:
  2029. want = color.RGBA{G: 0x80, B: 0x80, A: 0xff}
  2030. case ebiten.ColorScaleModePremultipliedAlpha:
  2031. want = color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}
  2032. }
  2033. if !sameColors(got, want, 2) {
  2034. t.Errorf("At(1, 0): got: %v, want: %v", got, want)
  2035. }
  2036. })
  2037. }
  2038. }
  2039. func TestImageDrawTrianglesShaderInterpolatesValues(t *testing.T) {
  2040. const w, h = 3, 1
  2041. src := ebiten.NewImage(w, h)
  2042. dst := ebiten.NewImage(w, h)
  2043. src.Fill(color.White)
  2044. vs := []ebiten.Vertex{
  2045. {
  2046. DstX: 0,
  2047. DstY: 0,
  2048. SrcX: 0,
  2049. SrcY: 0,
  2050. ColorR: 1,
  2051. ColorG: 0,
  2052. ColorB: 0,
  2053. ColorA: 0,
  2054. },
  2055. {
  2056. DstX: w,
  2057. DstY: 0,
  2058. SrcX: w,
  2059. SrcY: 0,
  2060. ColorR: 0,
  2061. ColorG: 1,
  2062. ColorB: 0,
  2063. ColorA: 1,
  2064. },
  2065. {
  2066. DstX: 0,
  2067. DstY: h,
  2068. SrcX: 0,
  2069. SrcY: h,
  2070. ColorR: 1,
  2071. ColorG: 0,
  2072. ColorB: 0,
  2073. ColorA: 0,
  2074. },
  2075. {
  2076. DstX: w,
  2077. DstY: h,
  2078. SrcX: w,
  2079. SrcY: h,
  2080. ColorR: 0,
  2081. ColorG: 1,
  2082. ColorB: 0,
  2083. ColorA: 1,
  2084. },
  2085. }
  2086. dst.Fill(color.RGBA{B: 0xff, A: 0xff})
  2087. op := &ebiten.DrawTrianglesShaderOptions{
  2088. Images: [4]*ebiten.Image{src, nil, nil, nil},
  2089. }
  2090. is := []uint16{0, 1, 2, 1, 2, 3}
  2091. shader, err := ebiten.NewShader([]byte(`
  2092. package main
  2093. func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
  2094. return color
  2095. }
  2096. `))
  2097. if err != nil {
  2098. t.Fatalf("could not compile shader: %v", err)
  2099. }
  2100. dst.DrawTrianglesShader(vs, is, shader, op)
  2101. got := dst.At(1, 0).(color.RGBA)
  2102. // Shaders get each color value interpolated independently.
  2103. want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}
  2104. if !sameColors(got, want, 2) {
  2105. t.Errorf("At(1, 0): got: %v, want: %v", got, want)
  2106. }
  2107. }
  2108. // Issue #1137
  2109. func TestImageDrawOver(t *testing.T) {
  2110. const (
  2111. w = 320
  2112. h = 240
  2113. )
  2114. dst := ebiten.NewImage(w, h)
  2115. src := image.NewUniform(color.RGBA{R: 0xff, A: 0xff})
  2116. // This must not cause infinite-loop.
  2117. draw.Draw(dst, dst.Bounds(), src, image.ZP, draw.Over)
  2118. for j := 0; j < h; j++ {
  2119. for i := 0; i < w; i++ {
  2120. got := dst.At(i, j)
  2121. want := color.RGBA{R: 0xff, A: 0xff}
  2122. if got != want {
  2123. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  2124. }
  2125. }
  2126. }
  2127. }
  2128. func TestImageDrawDisposedImage(t *testing.T) {
  2129. defer func() {
  2130. if r := recover(); r == nil {
  2131. t.Errorf("DrawImage must panic but not")
  2132. }
  2133. }()
  2134. dst := ebiten.NewImage(16, 16)
  2135. src := ebiten.NewImage(16, 16)
  2136. src.Dispose()
  2137. dst.DrawImage(src, nil)
  2138. }
  2139. func TestImageDrawDeallocatedImage(t *testing.T) {
  2140. dst := ebiten.NewImage(16, 16)
  2141. src := ebiten.NewImage(16, 16)
  2142. src.Deallocate()
  2143. // DrawImage must not panic.
  2144. dst.DrawImage(src, nil)
  2145. }
  2146. func TestImageDrawTrianglesDisposedImage(t *testing.T) {
  2147. defer func() {
  2148. if r := recover(); r == nil {
  2149. t.Errorf("DrawTriangles must panic but not")
  2150. }
  2151. }()
  2152. dst := ebiten.NewImage(16, 16)
  2153. src := ebiten.NewImage(16, 16)
  2154. src.Dispose()
  2155. vs := make([]ebiten.Vertex, 4)
  2156. is := []uint16{0, 1, 2, 1, 2, 3}
  2157. dst.DrawTriangles(vs, is, src, nil)
  2158. }
  2159. func TestImageDrawTrianglesDeallocateImage(t *testing.T) {
  2160. dst := ebiten.NewImage(16, 16)
  2161. src := ebiten.NewImage(16, 16)
  2162. src.Deallocate()
  2163. vs := make([]ebiten.Vertex, 4)
  2164. is := []uint16{0, 1, 2, 1, 2, 3}
  2165. // DrawTriangles must not panic.
  2166. dst.DrawTriangles(vs, is, src, nil)
  2167. }
  2168. // #1137
  2169. func BenchmarkImageDrawOver(b *testing.B) {
  2170. dst := ebiten.NewImage(16, 16)
  2171. src := image.NewUniform(color.Black)
  2172. for n := 0; n < b.N; n++ {
  2173. draw.Draw(dst, dst.Bounds(), src, image.ZP, draw.Over)
  2174. }
  2175. }
  2176. // Issue #1171
  2177. func TestImageFloatTranslate(t *testing.T) {
  2178. const w, h = 32, 32
  2179. for s := 2; s <= 8; s++ {
  2180. s := s
  2181. t.Run(fmt.Sprintf("scale%d", s), func(t *testing.T) {
  2182. check := func(src *ebiten.Image) {
  2183. dst := ebiten.NewImage(w*(s+1), h*(s+1))
  2184. dst.Fill(color.RGBA{R: 0xff, A: 0xff})
  2185. op := &ebiten.DrawImageOptions{}
  2186. op.GeoM.Scale(float64(s), float64(s))
  2187. op.GeoM.Translate(0, 0.501)
  2188. dst.DrawImage(src, op)
  2189. for j := 0; j < h*s+1; j++ {
  2190. for i := 0; i < w*s; i++ {
  2191. got := dst.At(i, j)
  2192. x := byte(0xff)
  2193. if j > 0 {
  2194. x = (byte(j) - 1) / byte(s)
  2195. }
  2196. want := color.RGBA{R: x, A: 0xff}
  2197. if got != want {
  2198. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  2199. }
  2200. }
  2201. }
  2202. }
  2203. t.Run("image", func(t *testing.T) {
  2204. src := ebiten.NewImage(w, h)
  2205. pix := make([]byte, 4*w*h)
  2206. for j := 0; j < h; j++ {
  2207. for i := 0; i < w; i++ {
  2208. pix[4*(j*w+i)] = byte(j)
  2209. pix[4*(j*w+i)+3] = 0xff
  2210. }
  2211. }
  2212. src.WritePixels(pix)
  2213. check(src)
  2214. })
  2215. t.Run("subimage", func(t *testing.T) {
  2216. src := ebiten.NewImage(w*s, h*s)
  2217. pix := make([]byte, 4*(w*s)*(h*s))
  2218. for j := 0; j < h*s; j++ {
  2219. for i := 0; i < w*s; i++ {
  2220. pix[4*(j*(w*s)+i)] = byte(j)
  2221. pix[4*(j*(w*s)+i)+3] = 0xff
  2222. }
  2223. }
  2224. src.WritePixels(pix)
  2225. check(src.SubImage(image.Rect(0, 0, w, h)).(*ebiten.Image))
  2226. })
  2227. })
  2228. }
  2229. }
  2230. // Issue #1213
  2231. func TestImageColorMCopy(t *testing.T) {
  2232. const w, h = 16, 16
  2233. dst := ebiten.NewImage(w, h)
  2234. src := ebiten.NewImage(w, h)
  2235. for k := 0; k < 256; k++ {
  2236. op := &ebiten.DrawImageOptions{}
  2237. op.ColorM.Translate(1, 1, 1, float64(k)/0xff)
  2238. op.Blend = ebiten.BlendCopy
  2239. dst.DrawImage(src, op)
  2240. for j := 0; j < h; j++ {
  2241. for i := 0; i < w; i++ {
  2242. got := dst.At(i, j).(color.RGBA)
  2243. want := color.RGBA{R: byte(k), G: byte(k), B: byte(k), A: byte(k)}
  2244. if !sameColors(got, want, 1) {
  2245. t.Fatalf("dst.At(%d, %d), k: %d: got %v, want %v", i, j, k, got, want)
  2246. }
  2247. }
  2248. }
  2249. }
  2250. }
  2251. // TODO: Do we have to guarantee this behavior? See #1222
  2252. func TestImageWritePixelsAndModifyPixels(t *testing.T) {
  2253. const w, h = 16, 16
  2254. dst := ebiten.NewImage(w, h)
  2255. src := ebiten.NewImage(w, h)
  2256. pix := make([]byte, 4*w*h)
  2257. for j := 0; j < h; j++ {
  2258. for i := 0; i < w; i++ {
  2259. idx := 4 * (i + j*w)
  2260. pix[idx] = 0xff
  2261. pix[idx+1] = 0
  2262. pix[idx+2] = 0
  2263. pix[idx+3] = 0xff
  2264. }
  2265. }
  2266. src.WritePixels(pix)
  2267. // Modify pix after WritePixels
  2268. for j := 0; j < h; j++ {
  2269. for i := 0; i < w; i++ {
  2270. idx := 4 * (i + j*w)
  2271. pix[idx] = 0
  2272. pix[idx+1] = 0xff
  2273. pix[idx+2] = 0
  2274. pix[idx+3] = 0xff
  2275. }
  2276. }
  2277. // Ensure that src's pixels are actually used
  2278. dst.DrawImage(src, nil)
  2279. for j := 0; j < h; j++ {
  2280. for i := 0; i < w; i++ {
  2281. got := src.At(i, j).(color.RGBA)
  2282. want := color.RGBA{R: 0xff, A: 0xff}
  2283. if got != want {
  2284. t.Errorf("src.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2285. }
  2286. }
  2287. }
  2288. }
  2289. func TestImageCompositeModeMultiply(t *testing.T) {
  2290. const w, h = 16, 16
  2291. dst := ebiten.NewImage(w, h)
  2292. src := ebiten.NewImage(w, h)
  2293. dst.Fill(color.RGBA{R: 0x10, G: 0x20, B: 0x30, A: 0x40})
  2294. src.Fill(color.RGBA{R: 0x50, G: 0x60, B: 0x70, A: 0x80})
  2295. op := &ebiten.DrawImageOptions{}
  2296. op.CompositeMode = ebiten.CompositeModeMultiply
  2297. dst.DrawImage(src, op)
  2298. for j := 0; j < h; j++ {
  2299. for i := 0; i < w; i++ {
  2300. got := dst.At(i, j).(color.RGBA)
  2301. want := color.RGBA{
  2302. R: byte(math.Floor((0x10 / 255.0) * (0x50 / 255.0) * 255)),
  2303. G: byte(math.Floor((0x20 / 255.0) * (0x60 / 255.0) * 255)),
  2304. B: byte(math.Floor((0x30 / 255.0) * (0x70 / 255.0) * 255)),
  2305. A: byte(math.Floor((0x40 / 255.0) * (0x80 / 255.0) * 255)),
  2306. }
  2307. if got != want {
  2308. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2309. }
  2310. }
  2311. }
  2312. }
  2313. // Issue #1269
  2314. func TestImageZeroTriangle(t *testing.T) {
  2315. const w, h = 16, 16
  2316. dst := ebiten.NewImage(w, h)
  2317. src := ebiten.NewImage(1, 1)
  2318. vs := []ebiten.Vertex{}
  2319. is := []uint16{}
  2320. dst.DrawTriangles(vs, is, src, nil)
  2321. }
  2322. // Issue #1398
  2323. func TestImageDrawImageTooBigScale(t *testing.T) {
  2324. dst := ebiten.NewImage(1, 1)
  2325. src := ebiten.NewImage(1, 1)
  2326. op := &ebiten.DrawImageOptions{}
  2327. op.GeoM.Scale(1e20, 1e20)
  2328. dst.DrawImage(src, op)
  2329. }
  2330. // Issue #1398
  2331. func TestImageDrawImageTooSmallScale(t *testing.T) {
  2332. dst := ebiten.NewImage(1, 1)
  2333. src := ebiten.NewImage(1, 1)
  2334. op := &ebiten.DrawImageOptions{}
  2335. op.Filter = ebiten.FilterLinear
  2336. op.GeoM.Scale(1e-10, 1e-10)
  2337. dst.DrawImage(src, op)
  2338. }
  2339. // Issue #1399
  2340. func TestImageDrawImageCannotAllocateImageForMipmap(t *testing.T) {
  2341. dst := ebiten.NewImage(1, 1)
  2342. src := ebiten.NewImage(maxImageSize, maxImageSize)
  2343. op := &ebiten.DrawImageOptions{}
  2344. op.GeoM.Scale(64, 64)
  2345. dst.DrawImage(src, op)
  2346. dst.At(0, 0)
  2347. }
  2348. func TestImageNewImageWithZeroSize(t *testing.T) {
  2349. defer func() {
  2350. if r := recover(); r == nil {
  2351. t.Errorf("DrawImage must panic but not")
  2352. }
  2353. }()
  2354. _ = ebiten.NewImage(0, 1)
  2355. }
  2356. func TestImageNewImageFromImageWithZeroSize(t *testing.T) {
  2357. defer func() {
  2358. if r := recover(); r == nil {
  2359. t.Errorf("DrawImage must panic but not")
  2360. }
  2361. }()
  2362. img := image.NewRGBA(image.Rect(0, 0, 0, 1))
  2363. _ = ebiten.NewImageFromImage(img)
  2364. }
  2365. func TestImageClip(t *testing.T) {
  2366. const (
  2367. w = 16
  2368. h = 16
  2369. )
  2370. dst := ebiten.NewImage(w, h)
  2371. src := ebiten.NewImage(w, h)
  2372. dst.Fill(color.RGBA{R: 0xff, A: 0xff})
  2373. src.Fill(color.RGBA{G: 0xff, A: 0xff})
  2374. dst.SubImage(image.Rect(4, 5, 12, 14)).(*ebiten.Image).DrawImage(src, nil)
  2375. for j := 0; j < h; j++ {
  2376. for i := 0; i < w; i++ {
  2377. got := dst.At(i, j).(color.RGBA)
  2378. want := color.RGBA{R: 0xff, A: 0xff}
  2379. if 4 <= i && i < 12 && 5 <= j && j < 14 {
  2380. want = color.RGBA{G: 0xff, A: 0xff}
  2381. }
  2382. if got != want {
  2383. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2384. }
  2385. }
  2386. }
  2387. }
  2388. // Issue #1691
  2389. func TestImageSubImageFill(t *testing.T) {
  2390. dst := ebiten.NewImage(3, 3).SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
  2391. dst.Fill(color.White)
  2392. for j := 0; j < 3; j++ {
  2393. for i := 0; i < 3; i++ {
  2394. got := dst.At(i, j)
  2395. var want color.RGBA
  2396. if i == 1 && j == 1 {
  2397. want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  2398. }
  2399. if got != want {
  2400. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2401. }
  2402. }
  2403. }
  2404. dst = ebiten.NewImage(17, 31).SubImage(image.Rect(3, 4, 8, 10)).(*ebiten.Image)
  2405. dst.Fill(color.White)
  2406. for j := 0; j < 31; j++ {
  2407. for i := 0; i < 17; i++ {
  2408. got := dst.At(i, j)
  2409. var want color.RGBA
  2410. if 3 <= i && i < 8 && 4 <= j && j < 10 {
  2411. want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  2412. }
  2413. if got != want {
  2414. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2415. }
  2416. }
  2417. }
  2418. }
  2419. func TestImageEvenOdd(t *testing.T) {
  2420. whiteImage := ebiten.NewImage(3, 3)
  2421. whiteImage.Fill(color.White)
  2422. emptySubImage := whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
  2423. vs0 := []ebiten.Vertex{
  2424. {
  2425. DstX: 1, DstY: 1, SrcX: 1, SrcY: 1,
  2426. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2427. },
  2428. {
  2429. DstX: 15, DstY: 1, SrcX: 1, SrcY: 1,
  2430. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2431. },
  2432. {
  2433. DstX: 1, DstY: 15, SrcX: 1, SrcY: 1,
  2434. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2435. },
  2436. {
  2437. DstX: 15, DstY: 15, SrcX: 1, SrcY: 1,
  2438. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2439. },
  2440. }
  2441. is0 := []uint16{0, 1, 2, 1, 2, 3}
  2442. vs1 := []ebiten.Vertex{
  2443. {
  2444. DstX: 2, DstY: 2, SrcX: 1, SrcY: 1,
  2445. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2446. },
  2447. {
  2448. DstX: 14, DstY: 2, SrcX: 1, SrcY: 1,
  2449. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2450. },
  2451. {
  2452. DstX: 2, DstY: 14, SrcX: 1, SrcY: 1,
  2453. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2454. },
  2455. {
  2456. DstX: 14, DstY: 14, SrcX: 1, SrcY: 1,
  2457. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2458. },
  2459. }
  2460. is1 := []uint16{4, 5, 6, 5, 6, 7}
  2461. vs2 := []ebiten.Vertex{
  2462. {
  2463. DstX: 3, DstY: 3, SrcX: 1, SrcY: 1,
  2464. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2465. },
  2466. {
  2467. DstX: 13, DstY: 3, SrcX: 1, SrcY: 1,
  2468. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2469. },
  2470. {
  2471. DstX: 3, DstY: 13, SrcX: 1, SrcY: 1,
  2472. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2473. },
  2474. {
  2475. DstX: 13, DstY: 13, SrcX: 1, SrcY: 1,
  2476. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2477. },
  2478. }
  2479. is2 := []uint16{8, 9, 10, 9, 10, 11}
  2480. // Draw all the vertices once. The even-odd rule is applied for all the vertices once.
  2481. dst := ebiten.NewImage(16, 16)
  2482. op := &ebiten.DrawTrianglesOptions{
  2483. FillRule: ebiten.FillRuleEvenOdd,
  2484. }
  2485. dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
  2486. for j := 0; j < 16; j++ {
  2487. for i := 0; i < 16; i++ {
  2488. got := dst.At(i, j)
  2489. var want color.RGBA
  2490. switch {
  2491. case 3 <= i && i < 13 && 3 <= j && j < 13:
  2492. want = color.RGBA{B: 0xff, A: 0xff}
  2493. case 2 <= i && i < 14 && 2 <= j && j < 14:
  2494. want = color.RGBA{}
  2495. case 1 <= i && i < 15 && 1 <= j && j < 15:
  2496. want = color.RGBA{R: 0xff, A: 0xff}
  2497. default:
  2498. want = color.RGBA{}
  2499. }
  2500. if got != want {
  2501. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2502. }
  2503. }
  2504. }
  2505. // Do the same thing but with a little shift. This confirms that the underlying stencil buffer is cleared correctly.
  2506. for i := range vs0 {
  2507. vs0[i].DstX++
  2508. vs0[i].DstY++
  2509. }
  2510. for i := range vs1 {
  2511. vs1[i].DstX++
  2512. vs1[i].DstY++
  2513. }
  2514. for i := range vs2 {
  2515. vs2[i].DstX++
  2516. vs2[i].DstY++
  2517. }
  2518. dst.Clear()
  2519. dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
  2520. for j := 0; j < 16; j++ {
  2521. for i := 0; i < 16; i++ {
  2522. got := dst.At(i, j)
  2523. var want color.RGBA
  2524. switch {
  2525. case 4 <= i && i < 14 && 4 <= j && j < 14:
  2526. want = color.RGBA{B: 0xff, A: 0xff}
  2527. case 3 <= i && i < 15 && 3 <= j && j < 15:
  2528. want = color.RGBA{}
  2529. case 2 <= i && i < 16 && 2 <= j && j < 16:
  2530. want = color.RGBA{R: 0xff, A: 0xff}
  2531. default:
  2532. want = color.RGBA{}
  2533. }
  2534. if got != want {
  2535. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2536. }
  2537. }
  2538. }
  2539. // Do the same thing but with split DrawTriangle calls. This confirms that the even-odd rule is applied for one call.
  2540. for i := range vs0 {
  2541. vs0[i].DstX--
  2542. vs0[i].DstY--
  2543. }
  2544. for i := range vs1 {
  2545. vs1[i].DstX--
  2546. vs1[i].DstY--
  2547. }
  2548. for i := range vs2 {
  2549. vs2[i].DstX--
  2550. vs2[i].DstY--
  2551. }
  2552. dst.Clear()
  2553. // Use the first indices set.
  2554. dst.DrawTriangles(vs0, is0, emptySubImage, op)
  2555. dst.DrawTriangles(vs1, is0, emptySubImage, op)
  2556. dst.DrawTriangles(vs2, is0, emptySubImage, op)
  2557. for j := 0; j < 16; j++ {
  2558. for i := 0; i < 16; i++ {
  2559. got := dst.At(i, j)
  2560. var want color.RGBA
  2561. switch {
  2562. case 3 <= i && i < 13 && 3 <= j && j < 13:
  2563. want = color.RGBA{B: 0xff, A: 0xff}
  2564. case 2 <= i && i < 14 && 2 <= j && j < 14:
  2565. want = color.RGBA{G: 0xff, A: 0xff}
  2566. case 1 <= i && i < 15 && 1 <= j && j < 15:
  2567. want = color.RGBA{R: 0xff, A: 0xff}
  2568. default:
  2569. want = color.RGBA{}
  2570. }
  2571. if got != want {
  2572. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2573. }
  2574. }
  2575. }
  2576. }
  2577. func TestImageFillRule(t *testing.T) {
  2578. for _, fillRule := range []ebiten.FillRule{ebiten.FillRuleFillAll, ebiten.FillRuleNonZero, ebiten.FillRuleEvenOdd} {
  2579. fillRule := fillRule
  2580. var name string
  2581. switch fillRule {
  2582. case ebiten.FillRuleFillAll:
  2583. name = "FillAll"
  2584. case ebiten.FillRuleNonZero:
  2585. name = "NonZero"
  2586. case ebiten.FillRuleEvenOdd:
  2587. name = "EvenOdd"
  2588. }
  2589. t.Run(name, func(t *testing.T) {
  2590. whiteImage := ebiten.NewImage(3, 3)
  2591. whiteImage.Fill(color.White)
  2592. emptySubImage := whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
  2593. // The outside rectangle (clockwise)
  2594. vs0 := []ebiten.Vertex{
  2595. {
  2596. DstX: 1, DstY: 1, SrcX: 1, SrcY: 1,
  2597. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2598. },
  2599. {
  2600. DstX: 15, DstY: 1, SrcX: 1, SrcY: 1,
  2601. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2602. },
  2603. {
  2604. DstX: 15, DstY: 15, SrcX: 1, SrcY: 1,
  2605. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2606. },
  2607. {
  2608. DstX: 1, DstY: 15, SrcX: 1, SrcY: 1,
  2609. ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
  2610. },
  2611. }
  2612. is0 := []uint16{0, 1, 2, 2, 3, 0}
  2613. // An inside rectangle (clockwise)
  2614. vs1 := []ebiten.Vertex{
  2615. {
  2616. DstX: 2, DstY: 2, SrcX: 1, SrcY: 1,
  2617. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2618. },
  2619. {
  2620. DstX: 7, DstY: 2, SrcX: 1, SrcY: 1,
  2621. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2622. },
  2623. {
  2624. DstX: 7, DstY: 7, SrcX: 1, SrcY: 1,
  2625. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2626. },
  2627. {
  2628. DstX: 2, DstY: 7, SrcX: 1, SrcY: 1,
  2629. ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
  2630. },
  2631. }
  2632. is1 := []uint16{4, 5, 6, 6, 7, 4}
  2633. // An inside rectangle (counter-clockwise)
  2634. vs2 := []ebiten.Vertex{
  2635. {
  2636. DstX: 9, DstY: 9, SrcX: 1, SrcY: 1,
  2637. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2638. },
  2639. {
  2640. DstX: 14, DstY: 9, SrcX: 1, SrcY: 1,
  2641. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2642. },
  2643. {
  2644. DstX: 14, DstY: 14, SrcX: 1, SrcY: 1,
  2645. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2646. },
  2647. {
  2648. DstX: 9, DstY: 14, SrcX: 1, SrcY: 1,
  2649. ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
  2650. },
  2651. }
  2652. is2 := []uint16{8, 11, 10, 10, 9, 8}
  2653. // Draw all the vertices once. The even-odd rule is applied for all the vertices once.
  2654. dst := ebiten.NewImage(16, 16)
  2655. op := &ebiten.DrawTrianglesOptions{
  2656. FillRule: fillRule,
  2657. }
  2658. dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
  2659. for j := 0; j < 16; j++ {
  2660. for i := 0; i < 16; i++ {
  2661. got := dst.At(i, j)
  2662. var want color.RGBA
  2663. switch {
  2664. case 2 <= i && i < 7 && 2 <= j && j < 7:
  2665. if fillRule != ebiten.FillRuleEvenOdd {
  2666. want = color.RGBA{G: 0xff, A: 0xff}
  2667. }
  2668. case 9 <= i && i < 14 && 9 <= j && j < 14:
  2669. if fillRule == ebiten.FillRuleFillAll {
  2670. want = color.RGBA{B: 0xff, A: 0xff}
  2671. }
  2672. case 1 <= i && i < 15 && 1 <= j && j < 15:
  2673. want = color.RGBA{R: 0xff, A: 0xff}
  2674. }
  2675. if got != want {
  2676. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2677. }
  2678. }
  2679. }
  2680. // Do the same thing but with a little shift. This confirms that the underlying stencil buffer is cleared correctly.
  2681. for i := range vs0 {
  2682. vs0[i].DstX++
  2683. vs0[i].DstY++
  2684. }
  2685. for i := range vs1 {
  2686. vs1[i].DstX++
  2687. vs1[i].DstY++
  2688. }
  2689. for i := range vs2 {
  2690. vs2[i].DstX++
  2691. vs2[i].DstY++
  2692. }
  2693. dst.Clear()
  2694. dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
  2695. for j := 0; j < 16; j++ {
  2696. for i := 0; i < 16; i++ {
  2697. got := dst.At(i, j)
  2698. var want color.RGBA
  2699. switch {
  2700. case 3 <= i && i < 8 && 3 <= j && j < 8:
  2701. if fillRule != ebiten.FillRuleEvenOdd {
  2702. want = color.RGBA{G: 0xff, A: 0xff}
  2703. }
  2704. case 10 <= i && i < 15 && 10 <= j && j < 15:
  2705. if fillRule == ebiten.FillRuleFillAll {
  2706. want = color.RGBA{B: 0xff, A: 0xff}
  2707. }
  2708. case 2 <= i && i < 16 && 2 <= j && j < 16:
  2709. want = color.RGBA{R: 0xff, A: 0xff}
  2710. }
  2711. if got != want {
  2712. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2713. }
  2714. }
  2715. }
  2716. // Do the same thing but with split DrawTriangle calls. This confirms that fill rules are applied for one call.
  2717. for i := range vs0 {
  2718. vs0[i].DstX--
  2719. vs0[i].DstY--
  2720. }
  2721. for i := range vs1 {
  2722. vs1[i].DstX--
  2723. vs1[i].DstY--
  2724. }
  2725. for i := range vs2 {
  2726. vs2[i].DstX--
  2727. vs2[i].DstY--
  2728. }
  2729. dst.Clear()
  2730. dst.DrawTriangles(vs0, []uint16{0, 1, 2, 2, 3, 0}, emptySubImage, op)
  2731. dst.DrawTriangles(vs1, []uint16{0, 1, 2, 2, 3, 0}, emptySubImage, op)
  2732. dst.DrawTriangles(vs2, []uint16{0, 3, 2, 2, 1, 0}, emptySubImage, op)
  2733. for j := 0; j < 16; j++ {
  2734. for i := 0; i < 16; i++ {
  2735. got := dst.At(i, j)
  2736. var want color.RGBA
  2737. switch {
  2738. case 2 <= i && i < 7 && 2 <= j && j < 7:
  2739. want = color.RGBA{G: 0xff, A: 0xff}
  2740. case 9 <= i && i < 14 && 9 <= j && j < 14:
  2741. want = color.RGBA{B: 0xff, A: 0xff}
  2742. case 1 <= i && i < 15 && 1 <= j && j < 15:
  2743. want = color.RGBA{R: 0xff, A: 0xff}
  2744. }
  2745. if got != want {
  2746. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2747. }
  2748. }
  2749. }
  2750. })
  2751. }
  2752. }
  2753. // #1658
  2754. func BenchmarkColorMScale(b *testing.B) {
  2755. r := rand.Float64
  2756. dst := ebiten.NewImage(16, 16)
  2757. src := ebiten.NewImage(16, 16)
  2758. for n := 0; n < b.N; n++ {
  2759. op := &ebiten.DrawImageOptions{}
  2760. op.ColorM.Scale(r(), r(), r(), r())
  2761. dst.DrawImage(src, op)
  2762. }
  2763. }
  2764. func TestImageMoreIndicesThanMaxUint16(t *testing.T) {
  2765. const (
  2766. w = 16
  2767. h = 16
  2768. )
  2769. dst := ebiten.NewImage(w, h)
  2770. src := ebiten.NewImage(w, h)
  2771. src.Fill(color.White)
  2772. op := &ebiten.DrawTrianglesOptions{}
  2773. vs := make([]ebiten.Vertex, 3)
  2774. is := make([]uint16, 65538)
  2775. dst.DrawTriangles(vs, is, src, op)
  2776. // The next draw call should work well (and this is likely batched).
  2777. vs = []ebiten.Vertex{
  2778. {
  2779. DstX: 0,
  2780. DstY: 0,
  2781. SrcX: 0,
  2782. SrcY: 0,
  2783. ColorR: 1,
  2784. ColorG: 1,
  2785. ColorB: 1,
  2786. ColorA: 1,
  2787. },
  2788. {
  2789. DstX: w,
  2790. DstY: 0,
  2791. SrcX: w,
  2792. SrcY: 0,
  2793. ColorR: 1,
  2794. ColorG: 1,
  2795. ColorB: 1,
  2796. ColorA: 1,
  2797. },
  2798. {
  2799. DstX: 0,
  2800. DstY: h,
  2801. SrcX: 0,
  2802. SrcY: h,
  2803. ColorR: 1,
  2804. ColorG: 1,
  2805. ColorB: 1,
  2806. ColorA: 1,
  2807. },
  2808. {
  2809. DstX: w,
  2810. DstY: h,
  2811. SrcX: w,
  2812. SrcY: h,
  2813. ColorR: 1,
  2814. ColorG: 1,
  2815. ColorB: 1,
  2816. ColorA: 1,
  2817. },
  2818. }
  2819. is = []uint16{0, 1, 2, 1, 2, 3}
  2820. dst.DrawTriangles(vs, is, src, op)
  2821. for j := 0; j < h; j++ {
  2822. for i := 0; i < w; i++ {
  2823. got := dst.At(i, j)
  2824. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  2825. if got != want {
  2826. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2827. }
  2828. }
  2829. }
  2830. }
  2831. func TestImageMoreVerticesThanMaxUint16(t *testing.T) {
  2832. const (
  2833. w = 16
  2834. h = 16
  2835. )
  2836. dst := ebiten.NewImage(w, h)
  2837. src := ebiten.NewImage(w, h)
  2838. src.Fill(color.White)
  2839. op := &ebiten.DrawTrianglesOptions{}
  2840. vs := make([]ebiten.Vertex, math.MaxUint16+1)
  2841. is := make([]uint16, 3)
  2842. dst.DrawTriangles(vs, is, src, op)
  2843. // The next draw call should work well (and this is likely batched).
  2844. vs = []ebiten.Vertex{
  2845. {
  2846. DstX: 0,
  2847. DstY: 0,
  2848. SrcX: 0,
  2849. SrcY: 0,
  2850. ColorR: 1,
  2851. ColorG: 1,
  2852. ColorB: 1,
  2853. ColorA: 1,
  2854. },
  2855. {
  2856. DstX: w,
  2857. DstY: 0,
  2858. SrcX: w,
  2859. SrcY: 0,
  2860. ColorR: 1,
  2861. ColorG: 1,
  2862. ColorB: 1,
  2863. ColorA: 1,
  2864. },
  2865. {
  2866. DstX: 0,
  2867. DstY: h,
  2868. SrcX: 0,
  2869. SrcY: h,
  2870. ColorR: 1,
  2871. ColorG: 1,
  2872. ColorB: 1,
  2873. ColorA: 1,
  2874. },
  2875. {
  2876. DstX: w,
  2877. DstY: h,
  2878. SrcX: w,
  2879. SrcY: h,
  2880. ColorR: 1,
  2881. ColorG: 1,
  2882. ColorB: 1,
  2883. ColorA: 1,
  2884. },
  2885. }
  2886. is = []uint16{0, 1, 2, 1, 2, 3}
  2887. dst.DrawTriangles(vs, is, src, op)
  2888. for j := 0; j < h; j++ {
  2889. for i := 0; i < w; i++ {
  2890. got := dst.At(i, j)
  2891. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  2892. if got != want {
  2893. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2894. }
  2895. }
  2896. }
  2897. }
  2898. func TestImageNewImageFromEbitenImage(t *testing.T) {
  2899. const (
  2900. w = 16
  2901. h = 16
  2902. )
  2903. pix := make([]byte, 4*w*h)
  2904. for j := 0; j < h; j++ {
  2905. for i := 0; i < w; i++ {
  2906. idx := 4 * (i + j*w)
  2907. pix[idx] = byte(i)
  2908. pix[idx+1] = byte(j)
  2909. pix[idx+2] = 0
  2910. pix[idx+3] = 0xff
  2911. }
  2912. }
  2913. img0 := ebiten.NewImage(w, h)
  2914. img0.WritePixels(pix)
  2915. img1 := ebiten.NewImageFromImage(img0)
  2916. for j := 0; j < h; j++ {
  2917. for i := 0; i < w; i++ {
  2918. got := img1.At(i, j)
  2919. want := color.RGBA{R: byte(i), G: byte(j), A: 0xff}
  2920. if got != want {
  2921. t.Errorf("img1.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2922. }
  2923. }
  2924. }
  2925. img2 := ebiten.NewImageFromImage(img0.SubImage(image.Rect(4, 4, 12, 12)))
  2926. for j := 0; j < h/2; j++ {
  2927. for i := 0; i < w/2; i++ {
  2928. got := img2.At(i, j)
  2929. want := color.RGBA{R: byte(i + 4), G: byte(j + 4), A: 0xff}
  2930. if got != want {
  2931. t.Errorf("img1.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2932. }
  2933. }
  2934. }
  2935. }
  2936. func TestImageOptionsUnmanaged(t *testing.T) {
  2937. const (
  2938. w = 16
  2939. h = 16
  2940. )
  2941. pix := make([]byte, 4*w*h)
  2942. for j := 0; j < h; j++ {
  2943. for i := 0; i < w; i++ {
  2944. idx := 4 * (i + j*w)
  2945. pix[idx] = byte(i)
  2946. pix[idx+1] = byte(j)
  2947. pix[idx+2] = 0
  2948. pix[idx+3] = 0xff
  2949. }
  2950. }
  2951. op := &ebiten.NewImageOptions{
  2952. Unmanaged: true,
  2953. }
  2954. img := ebiten.NewImageWithOptions(image.Rect(0, 0, w, h), op)
  2955. img.WritePixels(pix)
  2956. for j := 0; j < h; j++ {
  2957. for i := 0; i < w; i++ {
  2958. got := img.At(i, j)
  2959. want := color.RGBA{R: byte(i), G: byte(j), A: 0xff}
  2960. if got != want {
  2961. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2962. }
  2963. }
  2964. }
  2965. }
  2966. func TestImageOptionsNegativeBoundsWritePixels(t *testing.T) {
  2967. const (
  2968. w = 16
  2969. h = 16
  2970. )
  2971. pix0 := make([]byte, 4*w*h)
  2972. for j := 0; j < h; j++ {
  2973. for i := 0; i < w; i++ {
  2974. idx := 4 * (i + j*w)
  2975. pix0[idx] = byte(i)
  2976. pix0[idx+1] = byte(j)
  2977. pix0[idx+2] = 0
  2978. pix0[idx+3] = 0xff
  2979. }
  2980. }
  2981. const offset = -8
  2982. img := ebiten.NewImageWithOptions(image.Rect(offset, offset, w+offset, h+offset), nil)
  2983. img.WritePixels(pix0)
  2984. for j := offset; j < h+offset; j++ {
  2985. for i := offset; i < w+offset; i++ {
  2986. got := img.At(i, j)
  2987. want := color.RGBA{R: byte(i - offset), G: byte(j - offset), A: 0xff}
  2988. if got != want {
  2989. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  2990. }
  2991. }
  2992. }
  2993. pix1 := make([]byte, 4*(w/2)*(h/2))
  2994. for j := 0; j < h/2; j++ {
  2995. for i := 0; i < w/2; i++ {
  2996. idx := 4 * (i + j*w/2)
  2997. pix1[idx] = 0
  2998. pix1[idx+1] = 0
  2999. pix1[idx+2] = 0xff
  3000. pix1[idx+3] = 0xff
  3001. }
  3002. }
  3003. const offset2 = -4
  3004. sub := image.Rect(offset2, offset2, w/2+offset2, h/2+offset2)
  3005. img.SubImage(sub).(*ebiten.Image).WritePixels(pix1)
  3006. for j := offset; j < h+offset; j++ {
  3007. for i := offset; i < w+offset; i++ {
  3008. got := img.At(i, j)
  3009. want := color.RGBA{R: byte(i - offset), G: byte(j - offset), A: 0xff}
  3010. if image.Pt(i, j).In(sub) {
  3011. want = color.RGBA{B: 0xff, A: 0xff}
  3012. }
  3013. if got != want {
  3014. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3015. }
  3016. }
  3017. }
  3018. }
  3019. func TestImageOptionsNegativeBoundsSet(t *testing.T) {
  3020. const (
  3021. w = 16
  3022. h = 16
  3023. )
  3024. pix0 := make([]byte, 4*w*h)
  3025. for j := 0; j < h; j++ {
  3026. for i := 0; i < w; i++ {
  3027. idx := 4 * (i + j*w)
  3028. pix0[idx] = byte(i)
  3029. pix0[idx+1] = byte(j)
  3030. pix0[idx+2] = 0
  3031. pix0[idx+3] = 0xff
  3032. }
  3033. }
  3034. const offset = -8
  3035. img := ebiten.NewImageWithOptions(image.Rect(offset, offset, w+offset, h+offset), nil)
  3036. img.WritePixels(pix0)
  3037. img.Set(-1, -2, color.RGBA{})
  3038. for j := offset; j < h+offset; j++ {
  3039. for i := offset; i < w+offset; i++ {
  3040. got := img.At(i, j)
  3041. want := color.RGBA{R: byte(i - offset), G: byte(j - offset), A: 0xff}
  3042. if i == -1 && j == -2 {
  3043. want = color.RGBA{}
  3044. }
  3045. if got != want {
  3046. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3047. }
  3048. }
  3049. }
  3050. }
  3051. func TestImageOptionsNegativeBoundsDrawImage(t *testing.T) {
  3052. const (
  3053. w = 16
  3054. h = 16
  3055. offset = -8
  3056. )
  3057. dst := ebiten.NewImageWithOptions(image.Rect(offset, offset, w+offset, h+offset), nil)
  3058. src := ebiten.NewImageWithOptions(image.Rect(-1, -1, 1, 1), nil)
  3059. pix := make([]byte, 4*2*2)
  3060. for i := range pix {
  3061. pix[i] = 0xff
  3062. }
  3063. src.WritePixels(pix)
  3064. op := &ebiten.DrawImageOptions{}
  3065. op.GeoM.Translate(-1, -1)
  3066. op.GeoM.Scale(2, 3)
  3067. dst.DrawImage(src, op)
  3068. for j := offset; j < h+offset; j++ {
  3069. for i := offset; i < w+offset; i++ {
  3070. got := dst.At(i, j)
  3071. var want color.RGBA
  3072. if -2 <= i && i < 2 && -3 <= j && j < 3 {
  3073. want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  3074. }
  3075. if got != want {
  3076. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3077. }
  3078. }
  3079. }
  3080. }
  3081. func TestImageOptionsNegativeBoundsDrawTriangles(t *testing.T) {
  3082. const (
  3083. w = 16
  3084. h = 16
  3085. offset = -8
  3086. )
  3087. dst := ebiten.NewImageWithOptions(image.Rect(offset, offset, w+offset, h+offset), nil)
  3088. src := ebiten.NewImageWithOptions(image.Rect(-1, -1, 1, 1), nil)
  3089. pix := make([]byte, 4*2*2)
  3090. for i := range pix {
  3091. pix[i] = 0xff
  3092. }
  3093. src.WritePixels(pix)
  3094. vs := []ebiten.Vertex{
  3095. {
  3096. DstX: -2,
  3097. DstY: -3,
  3098. SrcX: -1,
  3099. SrcY: -1,
  3100. ColorR: 1,
  3101. ColorG: 1,
  3102. ColorB: 1,
  3103. ColorA: 1,
  3104. },
  3105. {
  3106. DstX: 2,
  3107. DstY: -3,
  3108. SrcX: 1,
  3109. SrcY: -1,
  3110. ColorR: 1,
  3111. ColorG: 1,
  3112. ColorB: 1,
  3113. ColorA: 1,
  3114. },
  3115. {
  3116. DstX: -2,
  3117. DstY: 3,
  3118. SrcX: -1,
  3119. SrcY: 1,
  3120. ColorR: 1,
  3121. ColorG: 1,
  3122. ColorB: 1,
  3123. ColorA: 1,
  3124. },
  3125. {
  3126. DstX: 2,
  3127. DstY: 3,
  3128. SrcX: 1,
  3129. SrcY: 1,
  3130. ColorR: 1,
  3131. ColorG: 1,
  3132. ColorB: 1,
  3133. ColorA: 1,
  3134. },
  3135. }
  3136. is := []uint16{0, 1, 2, 1, 2, 3}
  3137. dst.DrawTriangles(vs, is, src, nil)
  3138. for j := offset; j < h+offset; j++ {
  3139. for i := offset; i < w+offset; i++ {
  3140. got := dst.At(i, j)
  3141. var want color.RGBA
  3142. if -2 <= i && i < 2 && -3 <= j && j < 3 {
  3143. want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  3144. }
  3145. if got != want {
  3146. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3147. }
  3148. }
  3149. }
  3150. }
  3151. func TestImageFromImageOptions(t *testing.T) {
  3152. r := image.Rect(-2, -3, 4, 5)
  3153. pix := make([]byte, 4*r.Dx()*r.Dy())
  3154. for i := range pix {
  3155. pix[i] = 0xff
  3156. }
  3157. src := &image.RGBA{
  3158. Pix: pix,
  3159. Stride: 4 * 2,
  3160. Rect: r,
  3161. }
  3162. op := &ebiten.NewImageFromImageOptions{
  3163. PreserveBounds: true,
  3164. }
  3165. img := ebiten.NewImageFromImageWithOptions(src, op)
  3166. if got, want := img.Bounds(), r; got != want {
  3167. t.Errorf("got: %v, want: %v", got, want)
  3168. }
  3169. for j := r.Min.Y; j < r.Max.Y; j++ {
  3170. for i := r.Min.X; i < r.Max.X; i++ {
  3171. got := img.At(i, j)
  3172. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  3173. if got != want {
  3174. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3175. }
  3176. }
  3177. }
  3178. }
  3179. func TestImageFromEbitenImageOptions(t *testing.T) {
  3180. r := image.Rect(-2, -3, 4, 5)
  3181. src := ebiten.NewImageWithOptions(r, nil)
  3182. pix := make([]byte, 4*r.Dx()*r.Dy())
  3183. for i := range pix {
  3184. pix[i] = 0xff
  3185. }
  3186. src.WritePixels(pix)
  3187. op := &ebiten.NewImageFromImageOptions{
  3188. PreserveBounds: true,
  3189. }
  3190. img := ebiten.NewImageFromImageWithOptions(src, op)
  3191. if got, want := img.Bounds(), r; got != want {
  3192. t.Errorf("got: %v, want: %v", got, want)
  3193. }
  3194. for j := r.Min.Y; j < r.Max.Y; j++ {
  3195. for i := r.Min.X; i < r.Max.X; i++ {
  3196. got := img.At(i, j)
  3197. want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  3198. if got != want {
  3199. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3200. }
  3201. }
  3202. }
  3203. }
  3204. // Issue #2159
  3205. func TestImageOptionsFill(t *testing.T) {
  3206. r0 := image.Rect(-2, -3, 4, 5)
  3207. img := ebiten.NewImageWithOptions(r0, nil)
  3208. img.Fill(color.RGBA{R: 0xff, A: 0xff})
  3209. for j := r0.Min.Y; j < r0.Max.Y; j++ {
  3210. for i := r0.Min.X; i < r0.Max.X; i++ {
  3211. got := img.At(i, j)
  3212. want := color.RGBA{R: 0xff, A: 0xff}
  3213. if got != want {
  3214. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3215. }
  3216. }
  3217. }
  3218. r1 := image.Rect(-1, -2, 3, 4)
  3219. img.SubImage(r1).(*ebiten.Image).Fill(color.RGBA{G: 0xff, A: 0xff})
  3220. for j := r0.Min.Y; j < r0.Max.Y; j++ {
  3221. for i := r0.Min.X; i < r0.Max.X; i++ {
  3222. got := img.At(i, j)
  3223. want := color.RGBA{R: 0xff, A: 0xff}
  3224. if image.Pt(i, j).In(r1) {
  3225. want = color.RGBA{G: 0xff, A: 0xff}
  3226. }
  3227. if got != want {
  3228. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3229. }
  3230. }
  3231. }
  3232. }
  3233. // Issue #2159
  3234. func TestImageOptionsClear(t *testing.T) {
  3235. r0 := image.Rect(-2, -3, 4, 5)
  3236. img := ebiten.NewImageWithOptions(r0, nil)
  3237. img.Fill(color.RGBA{R: 0xff, A: 0xff})
  3238. img.Clear()
  3239. for j := r0.Min.Y; j < r0.Max.Y; j++ {
  3240. for i := r0.Min.X; i < r0.Max.X; i++ {
  3241. got := img.At(i, j)
  3242. want := color.RGBA{}
  3243. if got != want {
  3244. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3245. }
  3246. }
  3247. }
  3248. img.Fill(color.RGBA{R: 0xff, A: 0xff})
  3249. r1 := image.Rect(-1, -2, 3, 4)
  3250. img.SubImage(r1).(*ebiten.Image).Clear()
  3251. for j := r0.Min.Y; j < r0.Max.Y; j++ {
  3252. for i := r0.Min.X; i < r0.Max.X; i++ {
  3253. got := img.At(i, j)
  3254. want := color.RGBA{R: 0xff, A: 0xff}
  3255. if image.Pt(i, j).In(r1) {
  3256. want = color.RGBA{}
  3257. }
  3258. if got != want {
  3259. t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3260. }
  3261. }
  3262. }
  3263. }
  3264. // Issue #2178
  3265. func TestImageTooManyDrawImage(t *testing.T) {
  3266. src := ebiten.NewImage(1, 1)
  3267. src.Fill(color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
  3268. const (
  3269. w = 256
  3270. h = 256
  3271. )
  3272. dst := ebiten.NewImage(w, h)
  3273. op := &ebiten.DrawImageOptions{}
  3274. for j := 0; j < h; j++ {
  3275. for i := 0; i < w; i++ {
  3276. op.GeoM.Reset()
  3277. op.GeoM.Translate(float64(i), float64(j))
  3278. dst.DrawImage(src, op)
  3279. }
  3280. }
  3281. for j := 0; j < h; j++ {
  3282. for i := 0; i < w; i++ {
  3283. if got, want := dst.At(i, j), (color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}); got != want {
  3284. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3285. }
  3286. }
  3287. }
  3288. }
  3289. func TestImageTooManyDrawImage2(t *testing.T) {
  3290. src := ebiten.NewImage(1, 1)
  3291. src.Fill(color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
  3292. const (
  3293. w = 512
  3294. h = 512
  3295. )
  3296. dst := ebiten.NewImage(w, h)
  3297. posToColor := func(i, j int) color.RGBA {
  3298. return color.RGBA{
  3299. R: byte(i),
  3300. G: byte(j),
  3301. B: 0xff,
  3302. A: 0xff,
  3303. }
  3304. }
  3305. op := &ebiten.DrawImageOptions{}
  3306. for j := 0; j < h; j++ {
  3307. for i := 0; i < w; i++ {
  3308. op.GeoM.Reset()
  3309. op.GeoM.Translate(float64(i), float64(j))
  3310. op.ColorScale.Reset()
  3311. op.ColorScale.ScaleWithColor(posToColor(i, j))
  3312. dst.DrawImage(src, op)
  3313. }
  3314. }
  3315. for j := 0; j < h; j++ {
  3316. for i := 0; i < w; i++ {
  3317. if got, want := dst.At(i, j).(color.RGBA), posToColor(i, j); !sameColors(got, want, 1) {
  3318. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3319. }
  3320. }
  3321. }
  3322. }
  3323. // Issue #2178
  3324. func TestImageTooManyDrawTriangles(t *testing.T) {
  3325. img := ebiten.NewImage(3, 3)
  3326. img.Fill(color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
  3327. src := img.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
  3328. const (
  3329. w = 128
  3330. h = 64
  3331. )
  3332. dst := ebiten.NewImage(w, h)
  3333. var vertices []ebiten.Vertex
  3334. var indices []uint16
  3335. for j := 0; j < h; j++ {
  3336. for i := 0; i < w; i++ {
  3337. n := uint16(len(vertices))
  3338. vertices = append(vertices,
  3339. ebiten.Vertex{
  3340. DstX: float32(i),
  3341. DstY: float32(j),
  3342. SrcX: 1,
  3343. SrcY: 1,
  3344. ColorR: 1,
  3345. ColorG: 1,
  3346. ColorB: 1,
  3347. ColorA: 1,
  3348. },
  3349. ebiten.Vertex{
  3350. DstX: float32(i) + 1,
  3351. DstY: float32(j),
  3352. SrcX: 2,
  3353. SrcY: 1,
  3354. ColorR: 1,
  3355. ColorG: 1,
  3356. ColorB: 1,
  3357. ColorA: 1,
  3358. },
  3359. ebiten.Vertex{
  3360. DstX: float32(i),
  3361. DstY: float32(j) + 1,
  3362. SrcX: 1,
  3363. SrcY: 2,
  3364. ColorR: 1,
  3365. ColorG: 1,
  3366. ColorB: 1,
  3367. ColorA: 1,
  3368. },
  3369. ebiten.Vertex{
  3370. DstX: float32(i) + 1,
  3371. DstY: float32(j) + 1,
  3372. SrcX: 2,
  3373. SrcY: 2,
  3374. ColorR: 1,
  3375. ColorG: 1,
  3376. ColorB: 1,
  3377. ColorA: 1,
  3378. },
  3379. )
  3380. indices = append(indices, n, n+1, n+2, n+1, n+2, n+3)
  3381. }
  3382. }
  3383. dst.DrawTriangles(vertices, indices, src, nil)
  3384. for j := 0; j < h; j++ {
  3385. for i := 0; i < w; i++ {
  3386. if got, want := dst.At(i, j), (color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}); got != want {
  3387. t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
  3388. }
  3389. }
  3390. }
  3391. }
  3392. func TestImageSetOverSet(t *testing.T) {
  3393. img := ebiten.NewImage(1, 1)
  3394. img.Set(0, 0, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
  3395. if got, want := img.At(0, 0), (color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}); got != want {
  3396. t.Errorf("got: %v, want: %v", got, want)
  3397. }
  3398. // Apply the change by 'Set' by calling DrawImage.
  3399. dummy := ebiten.NewImage(1, 1)
  3400. img.DrawImage(dummy, nil)
  3401. if got, want := img.At(0, 0), (color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}); got != want {
  3402. t.Errorf("got: %v, want: %v", got, want)
  3403. }
  3404. img.Set(0, 0, color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80})
  3405. if got, want := img.At(0, 0), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}); got != want {
  3406. t.Errorf("got: %v, want: %v", got, want)
  3407. }
  3408. // Apply the change by 'Set' again.
  3409. img.DrawImage(dummy, nil)
  3410. if got, want := img.At(0, 0), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}); got != want {
  3411. t.Errorf("got: %v, want: %v", got, want)
  3412. }
  3413. }
  3414. // Issue #2204
  3415. func TestImageTooManyConstantBuffersInDirectX(t *testing.T) {
  3416. src := ebiten.NewImage(3, 3)
  3417. src.Fill(color.White)
  3418. src = src.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
  3419. vs := []ebiten.Vertex{
  3420. {
  3421. DstX: 0, DstY: 0, SrcX: 1, SrcY: 1,
  3422. ColorR: 1, ColorG: 1, ColorB: 1, ColorA: 1,
  3423. },
  3424. {
  3425. DstX: 16, DstY: 0, SrcX: 1, SrcY: 1,
  3426. ColorR: 1, ColorG: 1, ColorB: 1, ColorA: 1,
  3427. },
  3428. {
  3429. DstX: 0, DstY: 16, SrcX: 1, SrcY: 1,
  3430. ColorR: 1, ColorG: 1, ColorB: 1, ColorA: 1,
  3431. },
  3432. {
  3433. DstX: 16, DstY: 16, SrcX: 1, SrcY: 1,
  3434. ColorR: 1, ColorG: 1, ColorB: 1, ColorA: 1,
  3435. },
  3436. }
  3437. is := []uint16{0, 1, 2, 1, 2, 3}
  3438. dst0 := ebiten.NewImage(16, 16)
  3439. dst1 := ebiten.NewImage(16, 16)
  3440. op := &ebiten.DrawTrianglesOptions{
  3441. FillRule: ebiten.FillRuleEvenOdd,
  3442. }
  3443. for i := 0; i < 100; i++ {
  3444. dst0.DrawTriangles(vs, is, src, op)
  3445. dst1.DrawTriangles(vs, is, src, op)
  3446. }
  3447. if got, want := dst0.At(0, 0), (color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}); got != want {
  3448. t.Errorf("got: %v, want: %v", got, want)
  3449. }
  3450. if got, want := dst1.At(0, 0), (color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}); got != want {
  3451. t.Errorf("got: %v, want: %v", got, want)
  3452. }
  3453. }
  3454. func TestImageColorMAndScale(t *testing.T) {
  3455. const w, h = 16, 16
  3456. src := ebiten.NewImage(w, h)
  3457. src.Fill(color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80})
  3458. vs := []ebiten.Vertex{
  3459. {
  3460. SrcX: 0,
  3461. SrcY: 0,
  3462. DstX: 0,
  3463. DstY: 0,
  3464. ColorR: 0.5,
  3465. ColorG: 0.25,
  3466. ColorB: 0.5,
  3467. ColorA: 0.75,
  3468. },
  3469. {
  3470. SrcX: w,
  3471. SrcY: 0,
  3472. DstX: w,
  3473. DstY: 0,
  3474. ColorR: 0.5,
  3475. ColorG: 0.25,
  3476. ColorB: 0.5,
  3477. ColorA: 0.75,
  3478. },
  3479. {
  3480. SrcX: 0,
  3481. SrcY: h,
  3482. DstX: 0,
  3483. DstY: h,
  3484. ColorR: 0.5,
  3485. ColorG: 0.25,
  3486. ColorB: 0.5,
  3487. ColorA: 0.75,
  3488. },
  3489. {
  3490. SrcX: w,
  3491. SrcY: h,
  3492. DstX: w,
  3493. DstY: h,
  3494. ColorR: 0.5,
  3495. ColorG: 0.25,
  3496. ColorB: 0.5,
  3497. ColorA: 0.75,
  3498. },
  3499. }
  3500. is := []uint16{0, 1, 2, 1, 2, 3}
  3501. for _, format := range []ebiten.ColorScaleMode{
  3502. ebiten.ColorScaleModeStraightAlpha,
  3503. ebiten.ColorScaleModePremultipliedAlpha,
  3504. } {
  3505. format := format
  3506. t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
  3507. dst := ebiten.NewImage(w, h)
  3508. op := &ebiten.DrawTrianglesOptions{}
  3509. op.ColorM.Translate(0.25, 0.25, 0.25, 0)
  3510. op.ColorScaleMode = format
  3511. dst.DrawTriangles(vs, is, src, op)
  3512. got := dst.At(0, 0).(color.RGBA)
  3513. alphaBeforeScale := 0.5
  3514. var want color.RGBA
  3515. switch format {
  3516. case ebiten.ColorScaleModeStraightAlpha:
  3517. want = color.RGBA{
  3518. R: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
  3519. G: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25 * 0.75)),
  3520. B: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
  3521. A: byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
  3522. }
  3523. case ebiten.ColorScaleModePremultipliedAlpha:
  3524. want = color.RGBA{
  3525. R: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
  3526. G: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25)),
  3527. B: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
  3528. A: byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
  3529. }
  3530. }
  3531. if !sameColors(got, want, 2) {
  3532. t.Errorf("got: %v, want: %v", got, want)
  3533. }
  3534. })
  3535. }
  3536. }
  3537. func TestImageBlendOperation(t *testing.T) {
  3538. const w, h = 16, 1
  3539. dst := ebiten.NewImage(w, h)
  3540. src := ebiten.NewImage(w, h)
  3541. dstColor := func(i int) (byte, byte, byte, byte) {
  3542. return byte(4 * i * 17), byte(4*i*17 + 1), byte(4*i*17 + 2), byte(4*i*17 + 3)
  3543. }
  3544. srcColor := func(i int) (byte, byte, byte, byte) {
  3545. return byte(4 * i * 13), byte(4*i*13 + 1), byte(4*i*13 + 2), byte(4*i*13 + 3)
  3546. }
  3547. clamp := func(x int) byte {
  3548. if x > 255 {
  3549. return 255
  3550. }
  3551. if x < 0 {
  3552. return 0
  3553. }
  3554. return byte(x)
  3555. }
  3556. dstPix := make([]byte, 4*w*h)
  3557. for i := 0; i < w; i++ {
  3558. r, g, b, a := dstColor(i)
  3559. dstPix[4*i] = r
  3560. dstPix[4*i+1] = g
  3561. dstPix[4*i+2] = b
  3562. dstPix[4*i+3] = a
  3563. }
  3564. srcPix := make([]byte, 4*w*h)
  3565. for i := 0; i < w; i++ {
  3566. r, g, b, a := srcColor(i)
  3567. srcPix[4*i] = r
  3568. srcPix[4*i+1] = g
  3569. srcPix[4*i+2] = b
  3570. srcPix[4*i+3] = a
  3571. }
  3572. src.WritePixels(srcPix)
  3573. operations := []ebiten.BlendOperation{
  3574. ebiten.BlendOperationAdd,
  3575. ebiten.BlendOperationSubtract,
  3576. ebiten.BlendOperationReverseSubtract,
  3577. }
  3578. for _, rgbOp := range operations {
  3579. for _, alphaOp := range operations {
  3580. // Reset the destination state.
  3581. dst.WritePixels(dstPix)
  3582. op := &ebiten.DrawImageOptions{}
  3583. op.Blend = ebiten.Blend{
  3584. BlendFactorSourceRGB: ebiten.BlendFactorOne,
  3585. BlendFactorSourceAlpha: ebiten.BlendFactorOne,
  3586. BlendFactorDestinationRGB: ebiten.BlendFactorOne,
  3587. BlendFactorDestinationAlpha: ebiten.BlendFactorOne,
  3588. BlendOperationRGB: rgbOp,
  3589. BlendOperationAlpha: alphaOp,
  3590. }
  3591. dst.DrawImage(src, op)
  3592. for i := 0; i < w; i++ {
  3593. got := dst.At(i, 0).(color.RGBA)
  3594. sr, sg, sb, sa := srcColor(i)
  3595. dr, dg, db, da := dstColor(i)
  3596. var want color.RGBA
  3597. switch rgbOp {
  3598. case ebiten.BlendOperationAdd:
  3599. want.R = clamp(int(sr) + int(dr))
  3600. want.G = clamp(int(sg) + int(dg))
  3601. want.B = clamp(int(sb) + int(db))
  3602. case ebiten.BlendOperationSubtract:
  3603. want.R = clamp(int(sr) - int(dr))
  3604. want.G = clamp(int(sg) - int(dg))
  3605. want.B = clamp(int(sb) - int(db))
  3606. case ebiten.BlendOperationReverseSubtract:
  3607. want.R = clamp(int(dr) - int(sr))
  3608. want.G = clamp(int(dg) - int(sg))
  3609. want.B = clamp(int(db) - int(sb))
  3610. }
  3611. switch alphaOp {
  3612. case ebiten.BlendOperationAdd:
  3613. want.A = clamp(int(sa) + int(da))
  3614. case ebiten.BlendOperationSubtract:
  3615. want.A = clamp(int(sa) - int(da))
  3616. case ebiten.BlendOperationReverseSubtract:
  3617. want.A = clamp(int(da) - int(sa))
  3618. }
  3619. if !sameColors(got, want, 1) {
  3620. t.Errorf("dst.At(%d, 0): operations: %d, %d: got: %v, want: %v", i, rgbOp, alphaOp, got, want)
  3621. }
  3622. }
  3623. }
  3624. }
  3625. }
  3626. func TestImageBlendOperationMinAndMax(t *testing.T) {
  3627. const w, h = 16, 1
  3628. dst := ebiten.NewImage(w, h)
  3629. src := ebiten.NewImage(w, h)
  3630. dstColor := func(i int) (byte, byte, byte, byte) {
  3631. return byte(4 * i * 17), byte(4*i*17 + 1), byte(4*i*17 + 2), byte(4*i*17 + 3)
  3632. }
  3633. srcColor := func(i int) (byte, byte, byte, byte) {
  3634. return byte(4 * i * 13), byte(4*i*13 + 1), byte(4*i*13 + 2), byte(4*i*13 + 3)
  3635. }
  3636. dstPix := make([]byte, 4*w*h)
  3637. for i := 0; i < w; i++ {
  3638. r, g, b, a := dstColor(i)
  3639. dstPix[4*i] = r
  3640. dstPix[4*i+1] = g
  3641. dstPix[4*i+2] = b
  3642. dstPix[4*i+3] = a
  3643. }
  3644. srcPix := make([]byte, 4*w*h)
  3645. for i := 0; i < w; i++ {
  3646. r, g, b, a := srcColor(i)
  3647. srcPix[4*i] = r
  3648. srcPix[4*i+1] = g
  3649. srcPix[4*i+2] = b
  3650. srcPix[4*i+3] = a
  3651. }
  3652. src.WritePixels(srcPix)
  3653. operations := []ebiten.BlendOperation{
  3654. ebiten.BlendOperationMin,
  3655. ebiten.BlendOperationMax,
  3656. }
  3657. for _, rgbOp := range operations {
  3658. for _, alphaOp := range operations {
  3659. // Reset the destination state.
  3660. dst.WritePixels(dstPix)
  3661. op := &ebiten.DrawImageOptions{}
  3662. // Use the default blend factors, and confirm that the factors are ignored.
  3663. op.Blend = ebiten.Blend{
  3664. BlendFactorSourceRGB: ebiten.BlendFactorDefault,
  3665. BlendFactorSourceAlpha: ebiten.BlendFactorDefault,
  3666. BlendFactorDestinationRGB: ebiten.BlendFactorDefault,
  3667. BlendFactorDestinationAlpha: ebiten.BlendFactorDefault,
  3668. BlendOperationRGB: rgbOp,
  3669. BlendOperationAlpha: alphaOp,
  3670. }
  3671. dst.DrawImage(src, op)
  3672. for i := 0; i < w; i++ {
  3673. got := dst.At(i, 0).(color.RGBA)
  3674. sr, sg, sb, sa := srcColor(i)
  3675. dr, dg, db, da := dstColor(i)
  3676. var want color.RGBA
  3677. switch rgbOp {
  3678. case ebiten.BlendOperationMin:
  3679. want.R = min(sr, dr)
  3680. want.G = min(sg, dg)
  3681. want.B = min(sb, db)
  3682. case ebiten.BlendOperationMax:
  3683. want.R = max(sr, dr)
  3684. want.G = max(sg, dg)
  3685. want.B = max(sb, db)
  3686. }
  3687. switch alphaOp {
  3688. case ebiten.BlendOperationMin:
  3689. want.A = min(sa, da)
  3690. case ebiten.BlendOperationMax:
  3691. want.A = max(sa, da)
  3692. }
  3693. if !sameColors(got, want, 1) {
  3694. t.Errorf("dst.At(%d, 0): operations: %d, %d: got: %v, want: %v", i, rgbOp, alphaOp, got, want)
  3695. }
  3696. }
  3697. }
  3698. }
  3699. }
  3700. func TestImageBlendFactor(t *testing.T) {
  3701. if skipTooSlowTests(t) {
  3702. return
  3703. }
  3704. const w, h = 16, 1
  3705. dst := ebiten.NewImage(w, h)
  3706. src := ebiten.NewImage(w, h)
  3707. dstColor := func(i int) (byte, byte, byte, byte) {
  3708. return byte(4 * i * 17), byte(4*i*17 + 1), byte(4*i*17 + 2), byte(4*i*17 + 3)
  3709. }
  3710. srcColor := func(i int) (byte, byte, byte, byte) {
  3711. return byte(4 * i * 13), byte(4*i*13 + 1), byte(4*i*13 + 2), byte(4*i*13 + 3)
  3712. }
  3713. colorToFloats := func(r, g, b, a byte) (float64, float64, float64, float64) {
  3714. return float64(r) / 0xff, float64(g) / 0xff, float64(b) / 0xff, float64(a) / 0xff
  3715. }
  3716. clamp := func(x int) byte {
  3717. if x > 255 {
  3718. return 255
  3719. }
  3720. if x < 0 {
  3721. return 0
  3722. }
  3723. return byte(x)
  3724. }
  3725. dstPix := make([]byte, 4*w*h)
  3726. for i := 0; i < w; i++ {
  3727. r, g, b, a := dstColor(i)
  3728. dstPix[4*i] = r
  3729. dstPix[4*i+1] = g
  3730. dstPix[4*i+2] = b
  3731. dstPix[4*i+3] = a
  3732. }
  3733. srcPix := make([]byte, 4*w*h)
  3734. for i := 0; i < w; i++ {
  3735. r, g, b, a := srcColor(i)
  3736. srcPix[4*i] = r
  3737. srcPix[4*i+1] = g
  3738. srcPix[4*i+2] = b
  3739. srcPix[4*i+3] = a
  3740. }
  3741. src.WritePixels(srcPix)
  3742. factors := []ebiten.BlendFactor{
  3743. ebiten.BlendFactorZero,
  3744. ebiten.BlendFactorOne,
  3745. ebiten.BlendFactorSourceColor,
  3746. ebiten.BlendFactorOneMinusSourceColor,
  3747. ebiten.BlendFactorSourceAlpha,
  3748. ebiten.BlendFactorOneMinusSourceAlpha,
  3749. ebiten.BlendFactorDestinationColor,
  3750. ebiten.BlendFactorOneMinusDestinationColor,
  3751. ebiten.BlendFactorDestinationAlpha,
  3752. ebiten.BlendFactorOneMinusDestinationAlpha,
  3753. }
  3754. for _, srcRGBFactor := range factors {
  3755. for _, srcAlphaFactor := range factors {
  3756. for _, dstRGBFactor := range factors {
  3757. for _, dstAlphaFactor := range factors {
  3758. // Reset the destination state.
  3759. dst.WritePixels(dstPix)
  3760. op := &ebiten.DrawImageOptions{}
  3761. op.Blend = ebiten.Blend{
  3762. BlendFactorSourceRGB: srcRGBFactor,
  3763. BlendFactorSourceAlpha: srcAlphaFactor,
  3764. BlendFactorDestinationRGB: dstRGBFactor,
  3765. BlendFactorDestinationAlpha: dstAlphaFactor,
  3766. BlendOperationRGB: ebiten.BlendOperationAdd,
  3767. BlendOperationAlpha: ebiten.BlendOperationAdd,
  3768. }
  3769. dst.DrawImage(src, op)
  3770. for i := 0; i < w; i++ {
  3771. got := dst.At(i, 0).(color.RGBA)
  3772. sr, sg, sb, sa := colorToFloats(srcColor(i))
  3773. dr, dg, db, da := colorToFloats(dstColor(i))
  3774. var r, g, b, a float64
  3775. switch srcRGBFactor {
  3776. case ebiten.BlendFactorZero:
  3777. r += 0 * sr
  3778. g += 0 * sg
  3779. b += 0 * sb
  3780. case ebiten.BlendFactorOne:
  3781. r += 1 * sr
  3782. g += 1 * sg
  3783. b += 1 * sb
  3784. case ebiten.BlendFactorSourceColor:
  3785. r += sr * sr
  3786. g += sg * sg
  3787. b += sb * sb
  3788. case ebiten.BlendFactorOneMinusSourceColor:
  3789. r += (1 - sr) * sr
  3790. g += (1 - sg) * sg
  3791. b += (1 - sb) * sb
  3792. case ebiten.BlendFactorSourceAlpha:
  3793. r += sa * sr
  3794. g += sa * sg
  3795. b += sa * sb
  3796. case ebiten.BlendFactorOneMinusSourceAlpha:
  3797. r += (1 - sa) * sr
  3798. g += (1 - sa) * sg
  3799. b += (1 - sa) * sb
  3800. case ebiten.BlendFactorDestinationColor:
  3801. r += dr * sr
  3802. g += dg * sg
  3803. b += db * sb
  3804. case ebiten.BlendFactorOneMinusDestinationColor:
  3805. r += (1 - dr) * sr
  3806. g += (1 - dg) * sg
  3807. b += (1 - db) * sb
  3808. case ebiten.BlendFactorDestinationAlpha:
  3809. r += da * sr
  3810. g += da * sg
  3811. b += da * sb
  3812. case ebiten.BlendFactorOneMinusDestinationAlpha:
  3813. r += (1 - da) * sr
  3814. g += (1 - da) * sg
  3815. b += (1 - da) * sb
  3816. }
  3817. switch srcAlphaFactor {
  3818. case ebiten.BlendFactorZero:
  3819. a += 0 * sa
  3820. case ebiten.BlendFactorOne:
  3821. a += 1 * sa
  3822. case ebiten.BlendFactorSourceColor, ebiten.BlendFactorSourceAlpha:
  3823. a += sa * sa
  3824. case ebiten.BlendFactorOneMinusSourceColor, ebiten.BlendFactorOneMinusSourceAlpha:
  3825. a += (1 - sa) * sa
  3826. case ebiten.BlendFactorDestinationColor, ebiten.BlendFactorDestinationAlpha:
  3827. a += da * sa
  3828. case ebiten.BlendFactorOneMinusDestinationColor, ebiten.BlendFactorOneMinusDestinationAlpha:
  3829. a += (1 - da) * sa
  3830. }
  3831. switch dstRGBFactor {
  3832. case ebiten.BlendFactorZero:
  3833. r += 0 * dr
  3834. g += 0 * dg
  3835. b += 0 * db
  3836. case ebiten.BlendFactorOne:
  3837. r += 1 * dr
  3838. g += 1 * dg
  3839. b += 1 * db
  3840. case ebiten.BlendFactorSourceColor:
  3841. r += sr * dr
  3842. g += sg * dg
  3843. b += sb * db
  3844. case ebiten.BlendFactorOneMinusSourceColor:
  3845. r += (1 - sr) * dr
  3846. g += (1 - sg) * dg
  3847. b += (1 - sb) * db
  3848. case ebiten.BlendFactorSourceAlpha:
  3849. r += sa * dr
  3850. g += sa * dg
  3851. b += sa * db
  3852. case ebiten.BlendFactorOneMinusSourceAlpha:
  3853. r += (1 - sa) * dr
  3854. g += (1 - sa) * dg
  3855. b += (1 - sa) * db
  3856. case ebiten.BlendFactorDestinationColor:
  3857. r += dr * dr
  3858. g += dg * dg
  3859. b += db * db
  3860. case ebiten.BlendFactorOneMinusDestinationColor:
  3861. r += (1 - dr) * dr
  3862. g += (1 - dg) * dg
  3863. b += (1 - db) * db
  3864. case ebiten.BlendFactorDestinationAlpha:
  3865. r += da * dr
  3866. g += da * dg
  3867. b += da * db
  3868. case ebiten.BlendFactorOneMinusDestinationAlpha:
  3869. r += (1 - da) * dr
  3870. g += (1 - da) * dg
  3871. b += (1 - da) * db
  3872. }
  3873. switch dstAlphaFactor {
  3874. case ebiten.BlendFactorZero:
  3875. a += 0 * da
  3876. case ebiten.BlendFactorOne:
  3877. a += 1 * da
  3878. case ebiten.BlendFactorSourceColor, ebiten.BlendFactorSourceAlpha:
  3879. a += sa * da
  3880. case ebiten.BlendFactorOneMinusSourceColor, ebiten.BlendFactorOneMinusSourceAlpha:
  3881. a += (1 - sa) * da
  3882. case ebiten.BlendFactorDestinationColor, ebiten.BlendFactorDestinationAlpha:
  3883. a += da * da
  3884. case ebiten.BlendFactorOneMinusDestinationColor, ebiten.BlendFactorOneMinusDestinationAlpha:
  3885. a += (1 - da) * da
  3886. }
  3887. want := color.RGBA{
  3888. R: clamp(int(r * 0xff)),
  3889. G: clamp(int(g * 0xff)),
  3890. B: clamp(int(b * 0xff)),
  3891. A: clamp(int(a * 0xff)),
  3892. }
  3893. if !sameColors(got, want, 1) {
  3894. t.Errorf("dst.At(%d, 0): factors: %d, %d, %d, %d: got: %v, want: %v", i, srcRGBFactor, srcAlphaFactor, dstRGBFactor, dstAlphaFactor, got, want)
  3895. }
  3896. }
  3897. }
  3898. }
  3899. }
  3900. }
  3901. }
  3902. func TestImageAntiAlias(t *testing.T) {
  3903. // This value depends on internal/ui.bigOffscreenScale. Sync this.
  3904. const bigOffscreenScale = 2
  3905. const w, h = 272, 208
  3906. dst0 := ebiten.NewImage(w, h)
  3907. dst1 := ebiten.NewImage(w, h)
  3908. tmp := ebiten.NewImage(w*bigOffscreenScale, h*bigOffscreenScale)
  3909. src := ebiten.NewImage(3, 3)
  3910. src.Fill(color.RGBA{R: 0x24, G: 0x3f, B: 0x6a, A: 0x88})
  3911. for _, blend := range []ebiten.Blend{
  3912. {}, // Default
  3913. ebiten.BlendClear,
  3914. ebiten.BlendCopy,
  3915. ebiten.BlendSourceOver,
  3916. ebiten.BlendDestinationOver,
  3917. ebiten.BlendXor,
  3918. ebiten.BlendLighter,
  3919. } {
  3920. rnd := rand.New(rand.NewPCG(0, 0))
  3921. max := func(x, y, z byte) byte {
  3922. if x >= y && x >= z {
  3923. return x
  3924. }
  3925. if y >= x && y >= z {
  3926. return y
  3927. }
  3928. return z
  3929. }
  3930. dstPix := make([]byte, 4*w*h)
  3931. for i := 0; i < w*h; i++ {
  3932. n := rnd.Int()
  3933. r, g, b := byte(n), byte(n>>8), byte(n>>16)
  3934. a := max(r, g, b)
  3935. dstPix[4*i] = r
  3936. dstPix[4*i+1] = g
  3937. dstPix[4*i+2] = b
  3938. dstPix[4*i+3] = a
  3939. }
  3940. dst0.WritePixels(dstPix)
  3941. dst1.WritePixels(dstPix)
  3942. tmp.Clear()
  3943. // Create an actual result.
  3944. op := &ebiten.DrawTrianglesOptions{}
  3945. op.Blend = blend
  3946. op.AntiAlias = true
  3947. vs0 := []ebiten.Vertex{
  3948. {
  3949. DstX: w / 4,
  3950. DstY: h / 4,
  3951. SrcX: 1,
  3952. SrcY: 1,
  3953. ColorR: 1,
  3954. ColorG: 1,
  3955. ColorB: 1,
  3956. ColorA: 1,
  3957. },
  3958. {
  3959. DstX: 2 * w / 4,
  3960. DstY: h / 4,
  3961. SrcX: 2,
  3962. SrcY: 1,
  3963. ColorR: 1,
  3964. ColorG: 1,
  3965. ColorB: 1,
  3966. ColorA: 1,
  3967. },
  3968. {
  3969. DstX: w / 4,
  3970. DstY: 2 * h / 4,
  3971. SrcX: 1,
  3972. SrcY: 2,
  3973. ColorR: 1,
  3974. ColorG: 1,
  3975. ColorB: 1,
  3976. ColorA: 1,
  3977. },
  3978. }
  3979. is := []uint16{0, 1, 2}
  3980. dst0.DrawTriangles(vs0, is, src, op)
  3981. vs1 := []ebiten.Vertex{
  3982. {
  3983. DstX: 2 * w / 4,
  3984. DstY: 3 * h / 4,
  3985. SrcX: 1,
  3986. SrcY: 2,
  3987. ColorR: 1,
  3988. ColorG: 1,
  3989. ColorB: 1,
  3990. ColorA: 1,
  3991. },
  3992. {
  3993. DstX: 3 * w / 4,
  3994. DstY: 2 * h / 4,
  3995. SrcX: 2,
  3996. SrcY: 1,
  3997. ColorR: 1,
  3998. ColorG: 1,
  3999. ColorB: 1,
  4000. ColorA: 1,
  4001. },
  4002. {
  4003. DstX: 3 * w / 4,
  4004. DstY: 3 * h / 4,
  4005. SrcX: 2,
  4006. SrcY: 2,
  4007. ColorR: 1,
  4008. ColorG: 1,
  4009. ColorB: 1,
  4010. ColorA: 1,
  4011. },
  4012. }
  4013. dst0.DrawTriangles(vs1, is, src, op)
  4014. // Create an expected result.
  4015. // Copy an enlarged destination image to the offscreen.
  4016. opCopy := &ebiten.DrawImageOptions{}
  4017. opCopy.GeoM.Scale(bigOffscreenScale, bigOffscreenScale)
  4018. opCopy.Blend = ebiten.BlendCopy
  4019. tmp.DrawImage(dst1, opCopy)
  4020. // Render the vertices onto the offscreen.
  4021. for i := range vs0 {
  4022. vs0[i].DstX *= 2
  4023. vs0[i].DstY *= 2
  4024. }
  4025. for i := range vs1 {
  4026. vs1[i].DstX *= 2
  4027. vs1[i].DstY *= 2
  4028. }
  4029. op = &ebiten.DrawTrianglesOptions{}
  4030. op.Blend = blend
  4031. tmp.DrawTriangles(vs0, is, src, op)
  4032. tmp.DrawTriangles(vs1, is, src, op)
  4033. // Render a shrunk offscreen image onto the destination.
  4034. opShrink := &ebiten.DrawImageOptions{}
  4035. opShrink.GeoM.Scale(1.0/bigOffscreenScale, 1.0/bigOffscreenScale)
  4036. opShrink.Filter = ebiten.FilterLinear
  4037. opShrink.Blend = ebiten.BlendCopy
  4038. dst1.DrawImage(tmp, opShrink)
  4039. for j := 0; j < h; j++ {
  4040. for i := 0; i < w; i++ {
  4041. got := dst0.At(i, j).(color.RGBA)
  4042. want := dst1.At(i, j).(color.RGBA)
  4043. if !sameColors(got, want, 2) {
  4044. t.Errorf("At(%d, %d), blend: %v, got: %v, want: %v", i, j, blend, got, want)
  4045. }
  4046. }
  4047. }
  4048. }
  4049. }
  4050. func TestImageColorMScale(t *testing.T) {
  4051. const w, h = 16, 16
  4052. dst0 := ebiten.NewImage(w, h)
  4053. dst1 := ebiten.NewImage(w, h)
  4054. src := ebiten.NewImage(w, h)
  4055. src.Fill(color.RGBA{R: 0x24, G: 0x3f, B: 0x6a, A: 0x88})
  4056. // As the ColorM is a diagonal matrix, a built-in shader for a color matrix is NOT used.
  4057. op := &ebiten.DrawImageOptions{}
  4058. op.ColorM.Scale(0.3, 0.4, 0.5, 0.6)
  4059. dst0.DrawImage(src, op)
  4060. // As the ColorM is not a diagonal matrix, a built-in shader for a color matrix is used.
  4061. op = &ebiten.DrawImageOptions{}
  4062. op.ColorM.Scale(0.3, 0.4, 0.5, 0.6)
  4063. op.ColorM.Translate(0, 0, 0, 1e-4)
  4064. dst1.DrawImage(src, op)
  4065. got := dst0.At(0, 0)
  4066. want := dst1.At(0, 0)
  4067. if got != want {
  4068. t.Errorf("got: %v, want: %v", got, want)
  4069. }
  4070. }
  4071. func TestImageColorScaleAndColorM(t *testing.T) {
  4072. const w, h = 16, 16
  4073. dst0 := ebiten.NewImage(w, h)
  4074. dst1 := ebiten.NewImage(w, h)
  4075. src := ebiten.NewImage(w, h)
  4076. src.Fill(color.RGBA{R: 0x24, G: 0x3f, B: 0x6a, A: 0x88})
  4077. // ColorScale is applied to premultiplied-alpha colors.
  4078. op := &ebiten.DrawImageOptions{}
  4079. op.ColorScale.Scale(0.3*0.6, 0.4*0.6, 0.5*0.6, 0.6)
  4080. dst0.DrawImage(src, op)
  4081. // ColorM.Scale is applied to straight-alpha colors.
  4082. op = &ebiten.DrawImageOptions{}
  4083. op.ColorM.Scale(0.3, 0.4, 0.5, 0.6)
  4084. dst1.DrawImage(src, op)
  4085. got := dst0.At(0, 0)
  4086. want := dst1.At(0, 0)
  4087. if got != want {
  4088. t.Errorf("got: %v, want: %v", got, want)
  4089. }
  4090. }
  4091. // Issue #2428
  4092. func TestImageSetAndSubImage(t *testing.T) {
  4093. const w, h = 16, 16
  4094. img := ebiten.NewImage(w, h)
  4095. img.Set(1, 1, color.RGBA{R: 0xff, A: 0xff})
  4096. got := img.SubImage(image.Rect(0, 0, w, h)).At(1, 1).(color.RGBA)
  4097. want := color.RGBA{R: 0xff, A: 0xff}
  4098. if got != want {
  4099. t.Errorf("got: %v, want: %v", got, want)
  4100. }
  4101. }
  4102. // Issue #2611
  4103. func TestImageDrawTrianglesWithGreaterIndexThanVerticesCount(t *testing.T) {
  4104. defer func() {
  4105. if r := recover(); r == nil {
  4106. t.Errorf("DrawTriangles must panic but not")
  4107. }
  4108. }()
  4109. const w, h = 16, 16
  4110. dst := ebiten.NewImage(w, h)
  4111. src := ebiten.NewImage(w, h)
  4112. vs := make([]ebiten.Vertex, 4)
  4113. is := []uint16{0, 1, 2, 1, 2, 4}
  4114. dst.DrawTriangles(vs, is, src, nil)
  4115. }
  4116. // Issue #2611
  4117. func TestImageDrawTrianglesShaderWithGreaterIndexThanVerticesCount(t *testing.T) {
  4118. defer func() {
  4119. if r := recover(); r == nil {
  4120. t.Errorf("DrawTrianglesShader must panic but not")
  4121. }
  4122. }()
  4123. const w, h = 16, 16
  4124. dst := ebiten.NewImage(w, h)
  4125. vs := make([]ebiten.Vertex, 4)
  4126. is := []uint16{0, 1, 2, 1, 2, 4}
  4127. shader, err := ebiten.NewShader([]byte(`
  4128. package main
  4129. func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
  4130. return color
  4131. }
  4132. `))
  4133. if err != nil {
  4134. t.Fatalf("could not compile shader: %v", err)
  4135. }
  4136. dst.DrawTrianglesShader(vs, is, shader, nil)
  4137. }
  4138. // Issue #2733
  4139. func TestImageGeoMAfterDraw(t *testing.T) {
  4140. src := ebiten.NewImage(1, 1)
  4141. dst := ebiten.NewImageWithOptions(image.Rect(-1, -1, 0, 0), nil)
  4142. op0 := &ebiten.DrawImageOptions{}
  4143. dst.DrawImage(src, op0)
  4144. if x, y := op0.GeoM.Apply(0, 0); x != 0 || y != 0 {
  4145. t.Errorf("got: (%0.2f, %0.2f), want: (0, 0)", x, y)
  4146. }
  4147. s, err := ebiten.NewShader([]byte(`//kage:unit pixels
  4148. package main
  4149. func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
  4150. return vec4(1)
  4151. }
  4152. `))
  4153. if err != nil {
  4154. t.Fatal(err)
  4155. }
  4156. op1 := &ebiten.DrawRectShaderOptions{}
  4157. dst.DrawRectShader(1, 1, s, op1)
  4158. if x, y := op1.GeoM.Apply(0, 0); x != 0 || y != 0 {
  4159. t.Errorf("got: (%0.2f, %0.2f), want: (0, 0)", x, y)
  4160. }
  4161. }
  4162. func TestImageWritePixelAndDispose(t *testing.T) {
  4163. const (
  4164. w = 16
  4165. h = 16
  4166. )
  4167. img := ebiten.NewImage(w, h)
  4168. pix := make([]byte, 4*w*h)
  4169. for i := range pix {
  4170. pix[i] = 0xff
  4171. }
  4172. img.WritePixels(pix)
  4173. img.Dispose()
  4174. // Confirm that any pixel information is invalidated after Dispose is called.
  4175. if got, want := img.At(0, 0), (color.RGBA{}); got != want {
  4176. t.Errorf("got: %v, want: %v", got, want)
  4177. }
  4178. }
  4179. func TestImageWritePixelAndDeallocate(t *testing.T) {
  4180. const (
  4181. w = 16
  4182. h = 16
  4183. )
  4184. img := ebiten.NewImage(w, h)
  4185. pix := make([]byte, 4*w*h)
  4186. for i := range pix {
  4187. pix[i] = 0xff
  4188. }
  4189. img.WritePixels(pix)
  4190. img.Deallocate()
  4191. // Confirm that any pixel information is cleared after Deallocate is called.
  4192. if got, want := img.At(0, 0), (color.RGBA{}); got != want {
  4193. t.Errorf("got: %v, want: %v", got, want)
  4194. }
  4195. }
  4196. func TestImageDrawImageAfterDeallocation(t *testing.T) {
  4197. src, _, err := openEbitenImage()
  4198. if err != nil {
  4199. t.Fatal(err)
  4200. return
  4201. }
  4202. w, h := src.Bounds().Dx(), src.Bounds().Dy()
  4203. dst := ebiten.NewImage(w, h)
  4204. dst.DrawImage(src, nil)
  4205. for j := 0; j < h; j++ {
  4206. for i := 0; i < w; i++ {
  4207. got := dst.At(i, j)
  4208. want := src.At(i, j)
  4209. if got != want {
  4210. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  4211. }
  4212. }
  4213. }
  4214. // Even after deallocating the image, the image is still available.
  4215. dst.Deallocate()
  4216. dst.DrawImage(src, nil)
  4217. for j := 0; j < h; j++ {
  4218. for i := 0; i < w; i++ {
  4219. got := dst.At(i, j)
  4220. want := src.At(i, j)
  4221. if got != want {
  4222. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  4223. }
  4224. }
  4225. }
  4226. }
  4227. // Issue #2798
  4228. func TestImageInvalidPremultipliedAlphaColor(t *testing.T) {
  4229. // This test checks the rendering result when Set and WritePixels use an invalid premultiplied alpha color.
  4230. // The result values are kept and not clamped.
  4231. const (
  4232. w = 16
  4233. h = 16
  4234. )
  4235. dst := ebiten.NewImage(w, h)
  4236. dst.Set(0, 0, color.RGBA{R: 0xff, G: 0xc0, B: 0x80, A: 0x40})
  4237. dst.Set(0, 1, color.RGBA{R: 0xff, G: 0xc0, B: 0x80, A: 0x00})
  4238. if got, want := dst.At(0, 0).(color.RGBA), (color.RGBA{R: 0xff, G: 0xc0, B: 0x80, A: 0x40}); got != want {
  4239. t.Errorf("got: %v, want: %v", got, want)
  4240. }
  4241. if got, want := dst.At(0, 1).(color.RGBA), (color.RGBA{R: 0xff, G: 0xc0, B: 0x80, A: 0x00}); got != want {
  4242. t.Errorf("got: %v, want: %v", got, want)
  4243. }
  4244. pix := make([]byte, 4*w*h)
  4245. for j := 0; j < h; j++ {
  4246. for i := 0; i < w; i++ {
  4247. pix[4*(j*16+i)] = byte(i)
  4248. pix[4*(j*16+i)+1] = byte(j)
  4249. pix[4*(j*16+i)+2] = 0x80
  4250. pix[4*(j*16+i)+3] = byte(i - j)
  4251. }
  4252. }
  4253. dst.WritePixels(pix)
  4254. for j := 0; j < h; j++ {
  4255. for i := 0; i < w; i++ {
  4256. got := dst.At(i, j)
  4257. want := color.RGBA{R: byte(i), G: byte(j), B: 0x80, A: byte(i - j)}
  4258. if got != want {
  4259. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  4260. }
  4261. }
  4262. }
  4263. }