12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291 |
- /* RetroArch - A frontend for libretro.
- * Copyright (C) 2014-2018 - Jean-André Santoni
- * Copyright (C) 2011-2018 - Daniel De Matteis
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see <http://www.gnu.org/licenses/>.
- */
- #include <math.h>
- #include <string.h>
- #include <compat/strl.h>
- #include <encodings/utf.h>
- #include <retro_math.h>
- #include <retro_miscellaneous.h>
- #include <string/stdstring.h>
- #include <features/features_cpu.h>
- #include <lists/string_list.h>
- #include <array/rbuf.h>
- #include "gfx_animation.h"
- #include "../performance_counters.h"
- #define TICKER_SPEED 333333
- #define TICKER_SLOW_SPEED 1666666
- /* Pixel ticker nominally increases by one after each
- * TICKER_PIXEL_PERIOD ms (actual increase depends upon
- * ticker speed setting and display resolution)
- *
- * Formula is: (1.0f / 60.0f) * 1000.0f
- * */
- #define TICKER_PIXEL_PERIOD (16.666666666666668f)
- /* Mean human reading speed for all western languages,
- * characters per minute */
- #define TICKER_CPM 1000.0f
- /* Base time for which a line should be shown, in us */
- #define TICKER_LINE_DURATION_US(line_len) ((line_len * 60.0f * 1000.0f * 1000.0f) / TICKER_CPM)
- /* Base time for which a line should be shown, in ms */
- #define TICKER_LINE_DURATION_MS(line_len) ((line_len * 60.0f * 1000.0f) / TICKER_CPM)
- /* Ticker updates (nominally) once every TICKER_SPEED us
- * > Base number of ticks for which line should be shown */
- #define TICKER_LINE_DISPLAY_TICKS(line_len) ((size_t)(TICKER_LINE_DURATION_US(line_len) / (float)TICKER_SPEED))
- /* Smooth ticker updates (nominally) once every TICKER_PIXEL_PERIOD ms
- * > Base number of ticks for which text should scroll
- * from one line to the next */
- #define TICKER_LINE_SMOOTH_SCROLL_TICKS(line_len) ((size_t)(TICKER_LINE_DURATION_MS(line_len) / TICKER_PIXEL_PERIOD))
- static gfx_animation_t anim_st = {
- 0, /* ticker_idx */
- 0, /* ticker_slow_idx */
- 0, /* ticker_pixel_idx */
- 0, /* ticker_pixel_line_idx */
- 0, /* cur_time */
- 0, /* old_time */
- NULL, /* updatetime_cb */
- NULL, /* list */
- NULL, /* pending */
- 0.0f, /* delta_time */
- 0 /* flags */
- };
- gfx_animation_t *anim_get_ptr(void)
- {
- return &anim_st;
- }
- /* from https://github.com/kikito/tween.lua/blob/master/tween.lua */
- static float easing_linear(float t, float b, float c, float d)
- {
- return c * t / d + b;
- }
- static float easing_in_out_quad(float t, float b, float c, float d)
- {
- t = t / d * 2.0f;
- if (t < 1.0f)
- return c / 2.0f * t * t + b;
- return -c / 2.0f * ((t - 1.0f) * (t - 3.0f) - 1.0f) + b;
- }
- static float easing_in_quad(float t, float b, float c, float d)
- {
- float base = t / d;
- return c * (base * base) + b;
- }
- static float easing_out_quad(float t, float b, float c, float d)
- {
- t = t / d;
- return -c * t * (t - 2.0f) + b;
- }
- static float easing_out_in_quad(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_quad(t * 2.0f, b, c / 2.0f, d);
- return easing_in_quad((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_in_cubic(float t, float b, float c, float d)
- {
- float base = t / d;
- return c * (base * base * base) + b;
- }
- static float easing_out_cubic(float t, float b, float c, float d)
- {
- float base = t / d - 1.0f;
- return c * ((base * base * base) + 1.0f) + b;
- }
- static float easing_in_out_cubic(float t, float b, float c, float d)
- {
- t = t / d * 2.0f;
- if (t < 1.0f)
- return c / 2.0f * t * t * t + b;
- t = t - 2.0f;
- return c / 2.0f * (t * t * t + 2.0f) + b;
- }
- static float easing_out_in_cubic(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_cubic(t * 2.0f, b, c / 2.0f, d);
- return easing_in_cubic((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_in_quart(float t, float b, float c, float d)
- {
- float base = t / d;
- return c * (base * base * base * base) + b;
- }
- static float easing_out_quart(float t, float b, float c, float d)
- {
- float base = t / d - 1.0f;
- return -c * ((base * base * base * base) - 1.0f) + b;
- }
- static float easing_in_out_quart(float t, float b, float c, float d)
- {
- float base;
- t = t / d * 2.0f;
- if (t < 1.0f)
- return c / 2.0f * (t * t * t * t) + b;
- base = t - 2.0f;
- return -c / 2.0f * ((base * base * base * base) - 2.0f) + b;
- }
- static float easing_out_in_quart(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_quart(t * 2.0f, b, c / 2.0f, d);
- return easing_in_quart((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_in_quint(float t, float b, float c, float d)
- {
- float base = t / d;
- return c * (base * base * base * base * base) + b;
- }
- static float easing_out_quint(float t, float b, float c, float d)
- {
- float base = t / d - 1.0f;
- return c * ((base * base * base * base * base) + 1.0f) + b;
- }
- static float easing_in_out_quint(float t, float b, float c, float d)
- {
- float base;
- t = t / d * 2.0f;
- if (t < 1.0f)
- return c / 2.0f * (t * t * t * t * t) + b;
- base = t - 2.0f;
- return c / 2.0f * ((base * base * base * base * base) + 2.0f) + b;
- }
- static float easing_out_in_quint(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_quint(t * 2.0f, b, c / 2.0f, d);
- return easing_in_quint((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_in_sine(float t, float b, float c, float d)
- {
- return -c * cos(t / d * (M_PI / 2.0f)) + c + b;
- }
- static float easing_out_sine(float t, float b, float c, float d)
- {
- return c * sin(t / d * (M_PI / 2.0f)) + b;
- }
- static float easing_in_out_sine(float t, float b, float c, float d)
- {
- return -c / 2.0f * (cos(M_PI * t / d) - 1.0f) + b;
- }
- static float easing_out_in_sine(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_sine(t * 2.0f, b, c / 2.0f, d);
- return easing_in_sine((t * 2.0f) -d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_in_expo(float t, float b, float c, float d)
- {
- if (t == 0.0f)
- return b;
- return c * powf(2, 10 * (t / d - 1)) + b - c * 0.001;
- }
- static float easing_out_expo(float t, float b, float c, float d)
- {
- if (t == d)
- return b + c;
- return c * 1.001 * (-powf(2, -10 * t / d) + 1) + b;
- }
- static float easing_in_out_expo(float t, float b, float c, float d)
- {
- if (t == 0)
- return b;
- if (t == d)
- return b + c;
- t = t / d * 2.0f;
- if (t < 1.0f)
- return c / 2.0f * powf(2, 10 * (t - 1)) + b - c * 0.0005;
- return c / 2.0f * 1.0005 * (-powf(2, -10 * (t - 1)) + 2) + b;
- }
- static float easing_out_in_expo(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_expo(t * 2.0f, b, c / 2.0f, d);
- return easing_in_expo((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_in_circ(float t, float b, float c, float d)
- {
- float base = t / d;
- return(-c * (sqrtf(1.0f - (base * base)) - 1.0f) + b);
- }
- static float easing_out_circ(float t, float b, float c, float d)
- {
- float base = t / d - 1;
- return(c * sqrtf(1.0f - (base * base)) + b);
- }
- static float easing_in_out_circ(float t, float b, float c, float d)
- {
- t = t / d * 2.0f;
- if (t < 1.0f)
- return -c / 2 * (sqrtf(1.0f - t * t) - 1.0f) + b;
- t = t - 2.0f;
- return c / 2.0f * (sqrtf(1.0f - t * t) + 1.0f) + b;
- }
- static float easing_out_in_circ(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_circ(t * 2.0f, b, c / 2.0f, d);
- return easing_in_circ((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static float easing_out_bounce(float t, float b, float c, float d)
- {
- t = t / d;
- if (t < 1 / 2.75f)
- return c * (7.5625f * t * t) + b;
- if (t < 2 / 2.75f)
- {
- t = t - (1.5f / 2.75f);
- return c * (7.5625f * t * t + 0.75f) + b;
- }
- else if (t < 2.5f / 2.75f)
- {
- t = t - (2.25f / 2.75f);
- return c * (7.5625f * t * t + 0.9375f) + b;
- }
- t = t - (2.625f / 2.75f);
- return c * (7.5625f * t * t + 0.984375f) + b;
- }
- static float easing_in_bounce(float t, float b, float c, float d)
- {
- return c - easing_out_bounce(d - t, 0, c, d) + b;
- }
- static float easing_in_out_bounce(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_in_bounce(t * 2.0f, 0.0f, c, d) * 0.5f + b;
- return easing_out_bounce(t * 2.0f - d, 0.0f, c, d) * 0.5f + c * .5f + b;
- }
- static float easing_out_in_bounce(float t, float b, float c, float d)
- {
- if (t < d / 2.0f)
- return easing_out_bounce(t * 2.0f, b, c / 2.0f, d);
- return easing_in_bounce((t * 2.0f) - d, b + c / 2.0f, c / 2.0f, d);
- }
- static size_t gfx_animation_ticker_generic(uint64_t idx,
- size_t old_width)
- {
- const int phase_left_stop = 2;
- int ticker_period = (int)(2 * old_width + 4);
- int phase = idx % ticker_period;
- int phase_left_moving = (int)(phase_left_stop + old_width);
- int phase_right_stop = phase_left_moving + 2;
- if (phase < phase_left_stop)
- return 0;
- else if (phase < phase_left_moving) /* left offset? */
- return phase - phase_left_stop;
- else if (phase < phase_right_stop)
- return old_width;
- /* right offset */
- return (int)(old_width - (phase - phase_right_stop));
- }
- static void gfx_animation_ticker_loop(uint64_t idx,
- size_t max_width, size_t str_width, size_t spacer_width,
- size_t *offset1, size_t *width1,
- size_t *offset2, size_t *width2,
- size_t *offset3, size_t *width3)
- {
- int ticker_period = (int)(str_width + spacer_width);
- int phase = idx % ticker_period;
-
- /* Output offsets/widths are unsigned size_t, but it's
- * easier to perform the required calculations with ints,
- * so create some temporary variables... */
- /* Looping text is composed of up to three strings,
- * where string 1 and 2 are different regions of the
- * source text and string 2 is a spacer:
- *
- * |-----max_width-----|
- * [string 1][string 2][string 3]
- *
- * The following implementation could probably be optimised,
- * but any performance gains would be trivial compared with
- * all the string manipulation that has to happen afterwards...
- */
-
- /* String 1 */
- int offset = 0;
- int width = (int)(str_width - phase);
- if (width < 0)
- width = 0;
- else if ((width > (int)max_width))
- width = (int)max_width;
-
- if (phase < (int)str_width)
- offset = phase;
-
- *offset1 = offset;
- *width1 = width;
-
- /* String 2 */
- offset = (int)(phase - str_width);
- if (offset < 0)
- offset = 0;
- width = (int)(max_width - *width1);
- if (width > (int)spacer_width)
- width = (int)spacer_width;
- width -= offset;
-
- *offset2 = offset;
- *width2 = width;
-
- /* String 3 */
- width = (int)(max_width - (*width1 + *width2));
- if (width < 0)
- width = 0;
-
- /* Note: offset is always zero here so offset3 is
- * unnecessary - but include it anyway to preserve
- * symmetry... */
- *offset3 = 0;
- *width3 = width;
- }
- static unsigned get_ticker_smooth_generic_scroll_offset(
- uint64_t idx, unsigned str_width, unsigned field_width)
- {
- const unsigned pause_duration = 32;
- unsigned scroll_width = str_width - field_width;
- unsigned ticker_period = 2 * (scroll_width + pause_duration);
- unsigned phase = idx % ticker_period;
- /* Determine scroll offset */
- if (phase < pause_duration)
- return 0;
- else if (phase < ticker_period >> 1)
- return (phase - pause_duration);
- else if (phase < (ticker_period >> 1) + pause_duration)
- return ((ticker_period - (2 * pause_duration)) >> 1);
- return (ticker_period - phase);
- }
- /* 'Fixed width' font version of ticker_smooth_scan_characters() */
- static void ticker_smooth_scan_string_fw(
- size_t num_chars, unsigned glyph_width,
- unsigned field_width, unsigned scroll_offset,
- unsigned *char_offset, unsigned *num_chars_to_copy,
- unsigned *x_offset)
- {
- /* Initialise output variables to 'sane' values */
- *num_chars_to_copy = 0;
- /* Determine index of first character to copy */
- if (scroll_offset > 0)
- {
- *char_offset = (scroll_offset / glyph_width) + 1;
- *x_offset = glyph_width - (scroll_offset % glyph_width);
- }
- else
- {
- *char_offset = 0;
- *x_offset = 0;
- }
- /* Determine number of characters remaining in
- * string once offset has been subtracted */
- if (*char_offset < num_chars)
- {
- size_t chars_remaining = num_chars - *char_offset;
- /* Determine number of characters to copy */
- if ((chars_remaining > 0) && (field_width > *x_offset))
- {
- *num_chars_to_copy = (field_width - *x_offset) / glyph_width;
- if (*num_chars_to_copy > chars_remaining)
- *num_chars_to_copy = (unsigned)chars_remaining;
- }
- }
- }
- /* 'Fixed width' font version of gfx_animation_ticker_smooth_generic() */
- static void gfx_animation_ticker_smooth_generic_fw(uint64_t idx,
- unsigned str_width, size_t num_chars,
- unsigned glyph_width, unsigned field_width,
- unsigned *char_offset, unsigned *num_chars_to_copy, unsigned *x_offset)
- {
- /* Initialise output variables to 'sane' values */
- *char_offset = 0;
- *num_chars_to_copy = 0;
- *x_offset = 0;
- /* Sanity check */
- if (num_chars >= 1)
- {
- unsigned scroll_offset = get_ticker_smooth_generic_scroll_offset(
- idx, str_width, field_width);
- ticker_smooth_scan_string_fw(
- num_chars, glyph_width, field_width, scroll_offset,
- char_offset, num_chars_to_copy, x_offset);
- }
- }
- /* 'Fixed width' font version of gfx_animation_ticker_smooth_loop() */
- static void gfx_animation_ticker_smooth_loop_fw(uint64_t idx,
- unsigned str_width, size_t num_chars,
- unsigned spacer_width, size_t num_spacer_chars,
- unsigned glyph_width, unsigned field_width,
- unsigned *char_offset1, unsigned *num_chars_to_copy1,
- unsigned *char_offset2, unsigned *num_chars_to_copy2,
- unsigned *char_offset3, unsigned *num_chars_to_copy3,
- unsigned *x_offset)
- {
- unsigned ticker_period = str_width + spacer_width;
- unsigned phase = idx % ticker_period;
- unsigned remaining_width = field_width;
- /* Initialise output variables to 'sane' values */
- *char_offset1 = 0;
- *num_chars_to_copy1 = 0;
- *char_offset2 = 0;
- *num_chars_to_copy2 = 0;
- *char_offset3 = 0;
- *num_chars_to_copy3 = 0;
- *x_offset = 0;
- /* Sanity check */
- if ((num_chars < 1) || (num_spacer_chars < 1))
- return;
- /* Looping text is composed of up to three strings,
- * where string 1 and 2 are different regions of the
- * source text and string 2 is a spacer:
- *
- * |----field_width----|
- * [string 1][string 2][string 3]
- */
- /* String 1 */
- if (phase < str_width)
- {
- unsigned scroll_offset = phase;
- ticker_smooth_scan_string_fw(
- num_chars, glyph_width, remaining_width, scroll_offset,
- char_offset1, num_chars_to_copy1, x_offset);
- /* Update remaining width
- * Note: We can avoid all the display_width shenanigans
- * here (c.f. gfx_animation_ticker_smooth_loop()) because
- * the font width is constant - i.e. we don't have to wrangle
- * out the width of the last 'non-copied' character since it
- * is known a priori, so we can just subtract the string width
- * + offset here, and perform an 'if (remaining_width > glyph_width)'
- * for strings 2 and 3 */
- remaining_width -= (*x_offset + (*num_chars_to_copy1 * glyph_width));
- }
- /* String 2 */
- if (remaining_width > glyph_width)
- {
- unsigned scroll_offset = 0;
- unsigned x_offset2 = 0;
- /* Check whether we've passed the end of string 1 */
- if (phase > str_width)
- scroll_offset = phase - str_width;
- ticker_smooth_scan_string_fw(
- num_spacer_chars, glyph_width, remaining_width, scroll_offset,
- char_offset2, num_chars_to_copy2, &x_offset2);
- /* > Update remaining width */
- remaining_width -= (x_offset2 + (*num_chars_to_copy2 * glyph_width));
- /* If scroll_offset is greater than zero, it means
- * string 2 is the first string to be displayed
- * > ticker x offset is therefore string 2's offset */
- if (scroll_offset > 0)
- *x_offset = x_offset2;
- }
- /* String 3 */
- if (remaining_width > glyph_width)
- {
- /* String 3 is only shown when string 2 is shown,
- * so we can take some shortcuts... */
- *char_offset3 = 0;
- /* Determine number of characters to copy */
- *num_chars_to_copy3 = remaining_width / glyph_width;
- if (*num_chars_to_copy3 > num_chars)
- *num_chars_to_copy3 = (unsigned)num_chars;
- }
- }
- static void ticker_smooth_scan_characters(
- const unsigned *char_widths, size_t num_chars,
- unsigned field_width, unsigned scroll_offset,
- unsigned *char_offset, unsigned *num_chars_to_copy,
- unsigned *x_offset, unsigned *str_width,
- unsigned *display_width)
- {
- unsigned i;
- unsigned text_width = 0;
- unsigned scroll_pos = scroll_offset;
- bool deferred_str_width = true;
- /* Initialise output variables to 'sane' values */
- *char_offset = 0;
- *num_chars_to_copy = 0;
- *x_offset = 0;
- if (str_width)
- *str_width = 0;
- if (display_width)
- *display_width = 0;
- /* Determine index of first character to copy */
- if (scroll_pos > 0)
- {
- for (i = 0; i < num_chars; i++)
- {
- if (scroll_pos > char_widths[i])
- scroll_pos -= char_widths[i];
- else
- {
- /* Note: It's okay for char_offset to go out
- * of range here (num_chars_to_copy will be zero
- * in this case) */
- *char_offset = i + 1;
- *x_offset = char_widths[i] - scroll_pos;
- break;
- }
- }
- }
- /* Determine number of characters to copy */
- for (i = *char_offset; i < num_chars; i++)
- {
- text_width += char_widths[i];
- if (*x_offset + text_width <= field_width)
- (*num_chars_to_copy)++;
- else
- {
- /* Get actual width of resultant string
- * (excluding x offset + end padding)
- * Note that this is only set if we exceed the
- * field width - if all characters up to the end
- * of the string are copied... */
- if (str_width)
- {
- deferred_str_width = false;
- *str_width = text_width - char_widths[i];
- }
- break;
- }
- }
- /* ...then we have to update str_width here instead */
- if (str_width)
- if (deferred_str_width)
- *str_width = text_width;
- /* Get total display width of resultant string
- * (x offset + text width + end padding) */
- if (display_width)
- {
- *display_width = *x_offset + text_width;
- if (*display_width > field_width)
- *display_width = field_width;
- }
- }
- static void gfx_animation_ticker_smooth_generic(uint64_t idx,
- const unsigned *char_widths, size_t num_chars,
- unsigned str_width, unsigned field_width,
- unsigned *char_offset, unsigned *num_chars_to_copy,
- unsigned *x_offset, unsigned *dst_str_width)
- {
- /* Initialise output variables to 'sane' values */
- *char_offset = 0;
- *num_chars_to_copy = 0;
- *x_offset = 0;
- if (dst_str_width)
- *dst_str_width = 0;
- /* Sanity check */
- if (num_chars >= 1)
- {
- unsigned scroll_offset = get_ticker_smooth_generic_scroll_offset(
- idx, str_width, field_width);
- ticker_smooth_scan_characters(
- char_widths, num_chars, field_width, scroll_offset,
- char_offset, num_chars_to_copy, x_offset, dst_str_width, NULL);
- }
- }
- static void gfx_animation_ticker_smooth_loop(uint64_t idx,
- const unsigned *char_widths, size_t num_chars,
- const unsigned *spacer_widths, size_t num_spacer_chars,
- unsigned str_width, unsigned spacer_width, unsigned field_width,
- unsigned *char_offset1, unsigned *num_chars_to_copy1,
- unsigned *char_offset2, unsigned *num_chars_to_copy2,
- unsigned *char_offset3, unsigned *num_chars_to_copy3,
- unsigned *x_offset, unsigned *dst_str_width)
- {
- unsigned ticker_period = str_width + spacer_width;
- unsigned phase = idx % ticker_period;
- unsigned remaining_width = field_width;
- /* Initialise output variables to 'sane' values */
- *char_offset1 = 0;
- *num_chars_to_copy1 = 0;
- *char_offset2 = 0;
- *num_chars_to_copy2 = 0;
- *char_offset3 = 0;
- *num_chars_to_copy3 = 0;
- *x_offset = 0;
- if (dst_str_width)
- *dst_str_width = 0;
- /* Sanity check */
- if ((num_chars < 1) || (num_spacer_chars < 1))
- return;
- /* Looping text is composed of up to three strings,
- * where string 1 and 2 are different regions of the
- * source text and string 2 is a spacer:
- *
- * |----field_width----|
- * [string 1][string 2][string 3]
- */
- /* String 1 */
- if (phase < str_width)
- {
- unsigned scroll_offset = phase;
- unsigned display_width = 0;
- unsigned str1_width = 0;
- ticker_smooth_scan_characters(
- char_widths, num_chars, remaining_width, scroll_offset,
- char_offset1, num_chars_to_copy1, x_offset, &str1_width, &display_width);
- /* Update remaining width */
- remaining_width -= display_width;
- /* Update dst_str_width */
- if (dst_str_width)
- *dst_str_width += str1_width;
- }
- /* String 2 */
- if (remaining_width > 0)
- {
- unsigned scroll_offset = 0;
- unsigned display_width = 0;
- unsigned str2_width = 0;
- unsigned x_offset2 = 0;
- /* Check whether we've passed the end of string 1 */
- if (phase > str_width)
- scroll_offset = phase - str_width;
- ticker_smooth_scan_characters(
- spacer_widths, num_spacer_chars, remaining_width, scroll_offset,
- char_offset2, num_chars_to_copy2, &x_offset2, &str2_width, &display_width);
- /* > Update remaining width */
- remaining_width -= display_width;
- /* Update dst_str_width */
- if (dst_str_width)
- *dst_str_width += str2_width;
- /* If scroll_offset is greater than zero, it means
- * string 2 is the first string to be displayed
- * > ticker x offset is therefore string 2's offset */
- if (scroll_offset > 0)
- *x_offset = x_offset2;
- }
- /* String 3 */
- if (remaining_width > 0)
- {
- /* String 3 is only shown when string 2 is shown,
- * so we can take some shortcuts... */
- unsigned i;
- unsigned text_width = 0;
- *char_offset3 = 0;
- /* Determine number of characters to copy */
- for (i = 0; i < num_chars; i++)
- {
- text_width += char_widths[i];
- if (text_width <= remaining_width)
- (*num_chars_to_copy3)++;
- else
- {
- /* Update dst_str_width */
- if (dst_str_width)
- *dst_str_width += text_width - char_widths[i];
- break;
- }
- }
- }
- }
- static size_t gfx_animation_line_ticker_generic(uint64_t idx,
- size_t line_len, size_t max_lines, size_t num_lines)
- {
- size_t line_ticks = TICKER_LINE_DISPLAY_TICKS(line_len);
- /* Note: This function is only called if num_lines > max_lines */
- size_t excess_lines = num_lines - max_lines;
- /* Ticker will pause for one line duration when the first
- * or last line is reached (this is mostly required for the
- * case where num_lines == (max_lines + 1), since otherwise
- * the text flicks rapidly up and down in disconcerting
- * fashion...) */
- size_t ticker_period = (excess_lines * 2) + 2;
- size_t phase = (idx / line_ticks) % ticker_period;
- /* Pause on first line */
- if (phase > 0)
- phase--;
- /* Pause on last line */
- if (phase > excess_lines)
- phase--;
- /* Lines scrolling upwards */
- if (phase <= excess_lines)
- return phase;
- /* Lines scrolling downwards */
- return (excess_lines * 2) - phase;
- }
- static size_t gfx_animation_line_ticker_loop(uint64_t idx,
- size_t line_len, size_t num_lines)
- {
- size_t line_ticks = TICKER_LINE_DISPLAY_TICKS(line_len);
- size_t ticker_period = num_lines + 1;
- size_t phase = (idx / line_ticks) % ticker_period;
- /* In this case, line_offset is simply equal to the phase */
- return phase;
- }
- static void set_line_smooth_fade_parameters(
- bool scroll_up,
- size_t scroll_ticks, size_t line_phase, size_t line_height,
- size_t num_lines, size_t num_display_lines, size_t line_offset,
- float y_offset,
- size_t *top_fade_line_offset,
- float *top_fade_y_offset, float *top_fade_alpha,
- size_t *bottom_fade_line_offset,
- float *bottom_fade_y_offset, float *bottom_fade_alpha)
- {
- /* When a line fades out, alpha transitions from
- * 1 to 0 over the course of one half of the
- * scrolling line height. When a line fades in,
- * it's the other way around */
- float fade_out_alpha = ((float)scroll_ticks - ((float)line_phase * 2.0f)) / (float)scroll_ticks;
- float fade_in_alpha = -1.0f * fade_out_alpha;
- if (fade_out_alpha < 0.0f)
- fade_out_alpha = 0.0f;
- if (fade_in_alpha < 0.0f)
- fade_in_alpha = 0.0f;
- *top_fade_line_offset = (line_offset > 0) ? line_offset - 1 : num_lines;
- *top_fade_y_offset = y_offset - (float)line_height;
- if (scroll_up)
- {
- *top_fade_alpha = fade_out_alpha;
- *bottom_fade_alpha = fade_in_alpha;
- }
- else
- {
- *top_fade_alpha = fade_in_alpha;
- *bottom_fade_alpha = fade_out_alpha;
- }
- *bottom_fade_line_offset = line_offset + num_display_lines;
- *bottom_fade_y_offset = y_offset + (float)(line_height * num_display_lines);
- }
- static void set_line_smooth_fade_parameters_default(
- size_t *top_fade_line_offset, float *top_fade_y_offset, float *top_fade_alpha,
- size_t *bottom_fade_line_offset, float *bottom_fade_y_offset, float *bottom_fade_alpha)
- {
- *top_fade_line_offset = 0;
- *top_fade_y_offset = 0.0f;
- *top_fade_alpha = 0.0f;
- *bottom_fade_line_offset = 0;
- *bottom_fade_y_offset = 0.0f;
- *bottom_fade_alpha = 0.0f;
- }
- static void gfx_animation_line_ticker_smooth_generic(uint64_t idx,
- bool fade_enabled, size_t line_len, size_t line_height,
- size_t max_display_lines, size_t num_lines,
- size_t *num_display_lines, size_t *line_offset,
- float *y_offset,
- bool *fade_active,
- size_t *top_fade_line_offset, float *top_fade_y_offset,
- float *top_fade_alpha,
- size_t *bottom_fade_line_offset, float *bottom_fade_y_offset,
- float *bottom_fade_alpha)
- {
- size_t scroll_ticks = TICKER_LINE_SMOOTH_SCROLL_TICKS(line_len);
- /* Note: This function is only called if num_lines > max_display_lines */
- size_t excess_lines = num_lines - max_display_lines;
- /* Ticker will pause for one line duration when the first
- * or last line is reached */
- size_t ticker_period = ((excess_lines * 2) + 2) * scroll_ticks;
- size_t phase = idx % ticker_period;
- size_t line_phase = 0;
- bool pause = false;
- bool scroll_up = true;
- if (phase >= scroll_ticks)
- phase -= scroll_ticks;
- else
- {
- /* Pause on first line */
- pause = true;
- phase = 0;
- }
- /* Pause on last line and change direction */
- if (phase >= excess_lines * scroll_ticks)
- {
- scroll_up = false;
- if (phase < (excess_lines + 1) * scroll_ticks)
- {
- pause = true;
- phase = 0;
- }
- else
- phase -= (excess_lines + 1) * scroll_ticks;
- }
- line_phase = phase % scroll_ticks;
- if (pause)
- {
- /* Static display of max_display_lines
- * (no animation) */
- *num_display_lines = max_display_lines;
- *y_offset = 0.0f;
- *fade_active = false;
- *line_offset = scroll_up ? 0 : excess_lines;
- }
- else if (line_phase == 0)
- {
- /* Static display of max_display_lines
- * (no animation) */
- *num_display_lines = max_display_lines;
- *y_offset = 0.0f;
- *fade_active = false;
- *line_offset = scroll_up ? (phase / scroll_ticks) : (excess_lines - (phase / scroll_ticks));
- }
- else
- {
- /* Scroll animation is active */
- *num_display_lines = max_display_lines - 1;
- *fade_active = fade_enabled;
- if (scroll_up)
- {
- *line_offset = (phase / scroll_ticks) + 1;
- *y_offset = (float)line_height * (float)(scroll_ticks - line_phase) / (float)scroll_ticks;
- }
- else
- {
- *line_offset = excess_lines - (phase / scroll_ticks);
- *y_offset = (float)line_height * (1.0f - (float)(scroll_ticks - line_phase) / (float)scroll_ticks);
- }
- /* Set fade parameters if fade animation is active */
- if (*fade_active)
- set_line_smooth_fade_parameters(
- scroll_up, scroll_ticks, line_phase, line_height,
- num_lines, *num_display_lines, *line_offset, *y_offset,
- top_fade_line_offset, top_fade_y_offset, top_fade_alpha,
- bottom_fade_line_offset, bottom_fade_y_offset, bottom_fade_alpha);
- }
- /* Set 'default' fade parameters if fade animation
- * is inactive */
- if (!*fade_active)
- set_line_smooth_fade_parameters_default(
- top_fade_line_offset, top_fade_y_offset, top_fade_alpha,
- bottom_fade_line_offset, bottom_fade_y_offset, bottom_fade_alpha);
- }
- static void gfx_animation_line_ticker_smooth_loop(uint64_t idx,
- bool fade_enabled, size_t line_len, size_t line_height,
- size_t max_display_lines, size_t num_lines,
- size_t *num_display_lines, size_t *line_offset, float *y_offset,
- bool *fade_active,
- size_t *top_fade_line_offset, float *top_fade_y_offset, float *top_fade_alpha,
- size_t *bottom_fade_line_offset, float *bottom_fade_y_offset, float *bottom_fade_alpha)
- {
- size_t scroll_ticks = TICKER_LINE_SMOOTH_SCROLL_TICKS(line_len);
- size_t ticker_period = (num_lines + 1) * scroll_ticks;
- size_t phase = idx % ticker_period;
- size_t line_phase = phase % scroll_ticks;
- *line_offset = phase / scroll_ticks;
- if (line_phase == (scroll_ticks - 1))
- {
- /* Static display of max_display_lines
- * (no animation) */
- *num_display_lines = max_display_lines;
- *fade_active = false;
- }
- else
- {
- *num_display_lines = max_display_lines - 1;
- *fade_active = fade_enabled;
- }
- *y_offset = (float)line_height * (float)(scroll_ticks - line_phase) / (float)scroll_ticks;
- /* Set fade parameters */
- if (*fade_active)
- set_line_smooth_fade_parameters(
- true, scroll_ticks, line_phase, line_height,
- num_lines, *num_display_lines, *line_offset, *y_offset,
- top_fade_line_offset, top_fade_y_offset, top_fade_alpha,
- bottom_fade_line_offset, bottom_fade_y_offset, bottom_fade_alpha);
- else
- set_line_smooth_fade_parameters_default(
- top_fade_line_offset, top_fade_y_offset, top_fade_alpha,
- bottom_fade_line_offset, bottom_fade_y_offset, bottom_fade_alpha);
- }
- static void gfx_delayed_animation_cb(void *userdata)
- {
- gfx_delayed_animation_t *delayed_animation =
- (gfx_delayed_animation_t*) userdata;
- gfx_animation_push(&delayed_animation->entry);
- free(delayed_animation);
- }
- void gfx_animation_push_delayed(
- unsigned delay, gfx_animation_ctx_entry_t *entry)
- {
- gfx_timer_ctx_entry_t timer_entry;
- gfx_delayed_animation_t *delayed_animation = (gfx_delayed_animation_t*)
- malloc(sizeof(gfx_delayed_animation_t));
- memcpy(&delayed_animation->entry, entry, sizeof(gfx_animation_ctx_entry_t));
- timer_entry.cb = gfx_delayed_animation_cb;
- timer_entry.duration = delay;
- timer_entry.userdata = delayed_animation;
- gfx_animation_timer_start(&delayed_animation->timer, &timer_entry);
- }
- bool gfx_animation_push(gfx_animation_ctx_entry_t *entry)
- {
- struct tween t;
- gfx_animation_t *p_anim = &anim_st;
- t.duration = entry->duration;
- t.running_since = 0;
- t.initial_value = *entry->subject;
- t.target_value = entry->target_value;
- t.subject = entry->subject;
- t.tag = entry->tag;
- t.cb = entry->cb;
- t.userdata = entry->userdata;
- t.easing = NULL;
- t.deleted = false;
- switch (entry->easing_enum)
- {
- case EASING_LINEAR:
- t.easing = &easing_linear;
- break;
- /* Quad */
- case EASING_IN_QUAD:
- t.easing = &easing_in_quad;
- break;
- case EASING_OUT_QUAD:
- t.easing = &easing_out_quad;
- break;
- case EASING_IN_OUT_QUAD:
- t.easing = &easing_in_out_quad;
- break;
- case EASING_OUT_IN_QUAD:
- t.easing = &easing_out_in_quad;
- break;
- /* Cubic */
- case EASING_IN_CUBIC:
- t.easing = &easing_in_cubic;
- break;
- case EASING_OUT_CUBIC:
- t.easing = &easing_out_cubic;
- break;
- case EASING_IN_OUT_CUBIC:
- t.easing = &easing_in_out_cubic;
- break;
- case EASING_OUT_IN_CUBIC:
- t.easing = &easing_out_in_cubic;
- break;
- /* Quart */
- case EASING_IN_QUART:
- t.easing = &easing_in_quart;
- break;
- case EASING_OUT_QUART:
- t.easing = &easing_out_quart;
- break;
- case EASING_IN_OUT_QUART:
- t.easing = &easing_in_out_quart;
- break;
- case EASING_OUT_IN_QUART:
- t.easing = &easing_out_in_quart;
- break;
- /* Quint */
- case EASING_IN_QUINT:
- t.easing = &easing_in_quint;
- break;
- case EASING_OUT_QUINT:
- t.easing = &easing_out_quint;
- break;
- case EASING_IN_OUT_QUINT:
- t.easing = &easing_in_out_quint;
- break;
- case EASING_OUT_IN_QUINT:
- t.easing = &easing_out_in_quint;
- break;
- /* Sine */
- case EASING_IN_SINE:
- t.easing = &easing_in_sine;
- break;
- case EASING_OUT_SINE:
- t.easing = &easing_out_sine;
- break;
- case EASING_IN_OUT_SINE:
- t.easing = &easing_in_out_sine;
- break;
- case EASING_OUT_IN_SINE:
- t.easing = &easing_out_in_sine;
- break;
- /* Expo */
- case EASING_IN_EXPO:
- t.easing = &easing_in_expo;
- break;
- case EASING_OUT_EXPO:
- t.easing = &easing_out_expo;
- break;
- case EASING_IN_OUT_EXPO:
- t.easing = &easing_in_out_expo;
- break;
- case EASING_OUT_IN_EXPO:
- t.easing = &easing_out_in_expo;
- break;
- /* Circ */
- case EASING_IN_CIRC:
- t.easing = &easing_in_circ;
- break;
- case EASING_OUT_CIRC:
- t.easing = &easing_out_circ;
- break;
- case EASING_IN_OUT_CIRC:
- t.easing = &easing_in_out_circ;
- break;
- case EASING_OUT_IN_CIRC:
- t.easing = &easing_out_in_circ;
- break;
- /* Bounce */
- case EASING_IN_BOUNCE:
- t.easing = &easing_in_bounce;
- break;
- case EASING_OUT_BOUNCE:
- t.easing = &easing_out_bounce;
- break;
- case EASING_IN_OUT_BOUNCE:
- t.easing = &easing_in_out_bounce;
- break;
- case EASING_OUT_IN_BOUNCE:
- t.easing = &easing_out_in_bounce;
- break;
- default:
- break;
- }
- /* ignore born dead tweens */
- if (!t.easing || t.duration == 0 || t.initial_value == t.target_value)
- return false;
- if (p_anim->flags & GFX_ANIM_FLAG_IN_UPDATE)
- RBUF_PUSH(p_anim->pending, t);
- else
- RBUF_PUSH(p_anim->list, t);
- return true;
- }
- bool gfx_animation_update(
- retro_time_t current_time,
- bool timedate_enable,
- float _ticker_speed,
- unsigned video_width,
- unsigned video_height)
- {
- unsigned i;
- gfx_animation_t *p_anim = &anim_st;
- const bool ticker_is_active = p_anim->flags & GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
- static retro_time_t last_clock_update = 0;
- static retro_time_t last_ticker_update = 0;
- static retro_time_t last_ticker_slow_update = 0;
- /* Horizontal smooth ticker parameters */
- static float ticker_pixel_accumulator = 0.0f;
- unsigned ticker_pixel_accumulator_uint = 0;
- float ticker_pixel_increment = 0.0f;
- /* Vertical (line) smooth ticker parameters */
- static float ticker_pixel_line_accumulator = 0.0f;
- unsigned ticker_pixel_line_accumulator_uint = 0;
- float ticker_pixel_line_increment = 0.0f;
- /* Adjust ticker speed */
- float speed_factor =
- (_ticker_speed > 0.0001f) ? _ticker_speed : 1.0f;
- unsigned ticker_speed =
- (unsigned)(((float)TICKER_SPEED / speed_factor) + 0.5);
- unsigned ticker_slow_speed =
- (unsigned)(((float)TICKER_SLOW_SPEED / speed_factor) + 0.5);
- /* Note: cur_time & old_time are in us (microseconds),
- * delta_time is in ms */
- p_anim->cur_time = current_time;
- p_anim->delta_time = (p_anim->old_time == 0)
- ? 0.0f
- : (float)(p_anim->cur_time - p_anim->old_time) / 1000.0f;
- p_anim->old_time = p_anim->cur_time;
- if (((p_anim->cur_time - last_clock_update) > 1000000) /* 1000000 us == 1 second */
- && timedate_enable)
- {
- p_anim->flags |= GFX_ANIM_FLAG_IS_ACTIVE;
- last_clock_update = p_anim->cur_time;
- }
- if (ticker_is_active)
- {
- /* Update non-smooth ticker indices */
- if (p_anim->cur_time - last_ticker_update >= ticker_speed)
- {
- p_anim->ticker_idx++;
- last_ticker_update = p_anim->cur_time;
- }
- if (p_anim->cur_time - last_ticker_slow_update >= ticker_slow_speed)
- {
- p_anim->ticker_slow_idx++;
- last_ticker_slow_update = p_anim->cur_time;
- }
- /* Pixel tickers (horizontal + vertical/line) update
- * every frame (regardless of time delta), so require
- * special handling */
- /* > Get base increment size (+1 every TICKER_PIXEL_PERIOD ms) */
- ticker_pixel_increment = p_anim->delta_time / TICKER_PIXEL_PERIOD;
- /* > Apply ticker speed adjustment */
- ticker_pixel_increment *= speed_factor;
- /* At this point we diverge:
- * > Vertical (line) ticker is based upon text
- * characteristics (number of characters per
- * line) - it is therefore independent of display
- * size/scaling, so speed-adjusted pixel increment
- * is used directly */
- ticker_pixel_line_increment = ticker_pixel_increment;
- /* > Horizontal ticker is based upon physical line
- * width - it is therefore very much dependent upon
- * display size/scaling. Each menu driver is free
- * to handle video scaling as it pleases - a callback
- * function set by the menu driver is thus used to
- * perform menu-specific scaling adjustments */
- if (p_anim->updatetime_cb)
- p_anim->updatetime_cb(&ticker_pixel_increment,
- video_width, video_height);
- /* > Update accumulators */
- ticker_pixel_accumulator += ticker_pixel_increment;
- ticker_pixel_accumulator_uint = (unsigned)ticker_pixel_accumulator;
- ticker_pixel_line_accumulator += ticker_pixel_line_increment;
- ticker_pixel_line_accumulator_uint = (unsigned)ticker_pixel_line_accumulator;
- /* > Check whether we've accumulated enough
- * for an idx update */
- if (ticker_pixel_accumulator_uint > 0)
- {
- p_anim->ticker_pixel_idx += ticker_pixel_accumulator_uint;
- ticker_pixel_accumulator -= (float)ticker_pixel_accumulator_uint;
- }
- if (ticker_pixel_accumulator_uint > 0)
- {
- p_anim->ticker_pixel_line_idx += ticker_pixel_line_accumulator_uint;
- ticker_pixel_line_accumulator -= (float)ticker_pixel_line_accumulator_uint;
- }
- }
- p_anim->flags |= GFX_ANIM_FLAG_IN_UPDATE;
- p_anim->flags &= ~GFX_ANIM_FLAG_PENDING_DELETES;
- for (i = 0; i < RBUF_LEN(p_anim->list); i++)
- {
- struct tween *tween = &p_anim->list[i];
- if (tween->deleted)
- continue;
- tween->running_since += p_anim->delta_time;
- *tween->subject = tween->easing(
- tween->running_since,
- tween->initial_value,
- tween->target_value - tween->initial_value,
- tween->duration);
- if (tween->running_since >= tween->duration)
- {
- *tween->subject = tween->target_value;
- if (tween->cb)
- tween->cb(tween->userdata);
- RBUF_REMOVE(p_anim->list, i);
- i--;
- }
- }
- if (p_anim->flags & GFX_ANIM_FLAG_PENDING_DELETES)
- {
- for (i = 0; i < RBUF_LEN(p_anim->list); i++)
- {
- struct tween *tween = &p_anim->list[i];
- if (tween->deleted)
- {
- RBUF_REMOVE(p_anim->list, i);
- i--;
- }
- }
- p_anim->flags &= ~GFX_ANIM_FLAG_PENDING_DELETES;
- }
- if (RBUF_LEN(p_anim->pending) > 0)
- {
- size_t list_len = RBUF_LEN(p_anim->list);
- size_t pending_len = RBUF_LEN(p_anim->pending);
- RBUF_RESIZE(p_anim->list, list_len + pending_len);
- memcpy(p_anim->list + list_len, p_anim->pending,
- sizeof(*p_anim->pending) * pending_len);
- RBUF_CLEAR(p_anim->pending);
- }
- p_anim->flags &= ~GFX_ANIM_FLAG_IN_UPDATE;
- if (RBUF_LEN(p_anim->list) > 0)
- p_anim->flags |= GFX_ANIM_FLAG_IS_ACTIVE;
- else
- p_anim->flags &= ~GFX_ANIM_FLAG_IS_ACTIVE;
- return ((p_anim->flags & GFX_ANIM_FLAG_IS_ACTIVE) > 0);
- }
- static void build_ticker_loop_string(
- const char* src_str, const char *spacer,
- size_t char_offset1, size_t num_chars1,
- size_t char_offset2, size_t num_chars2,
- size_t char_offset3, size_t num_chars3,
- char *dest_str, size_t dest_str_len)
- {
- /* Copy 'trailing' chunk of source string, if required */
- if (num_chars1 > 0)
- utf8cpy(
- dest_str, dest_str_len,
- utf8skip(src_str, char_offset1), num_chars1);
- else
- dest_str[0] = '\0';
- /* Copy chunk of spacer string, if required */
- if (num_chars2 > 0)
- {
- char tmp[PATH_MAX_LENGTH];
- utf8cpy(
- tmp, sizeof(tmp),
- utf8skip(spacer, char_offset2), num_chars2);
- strlcat(dest_str, tmp, dest_str_len);
- }
- /* Copy 'leading' chunk of source string, if required */
- if (num_chars3 > 0)
- {
- char tmp[PATH_MAX_LENGTH];
- utf8cpy(
- tmp, sizeof(tmp),
- utf8skip(src_str, char_offset3), num_chars3);
- strlcat(dest_str, tmp, dest_str_len);
- }
- }
- static void build_line_ticker_string(
- size_t num_display_lines, size_t line_offset,
- struct string_list *lines,
- char *dest_str, size_t dest_str_len)
- {
- size_t i;
- for (i = 0; i < (num_display_lines-1); i++)
- {
- size_t offset = i + line_offset;
- size_t line_index = offset % (lines->size + 1);
- /* Is line valid? */
- if (line_index < lines->size)
- strlcat(dest_str, lines->elems[line_index].data, dest_str_len);
- strlcat(dest_str, "\n", dest_str_len);
- }
- {
- size_t offset = (num_display_lines-1) + line_offset;
- size_t line_index = offset % (lines->size + 1);
- /* Is line valid? */
- if (line_index < lines->size)
- strlcat(dest_str, lines->elems[line_index].data, dest_str_len);
- }
- }
- bool gfx_animation_ticker(gfx_animation_ctx_ticker_t *ticker)
- {
- gfx_animation_t *p_anim = &anim_st;
- size_t str_len = utf8len(ticker->str);
- if (!ticker->spacer)
- ticker->spacer = TICKER_SPACER_DEFAULT;
- if ((size_t)str_len <= ticker->len)
- {
- utf8cpy(ticker->s,
- PATH_MAX_LENGTH,
- ticker->str,
- ticker->len);
- return false;
- }
- if (!ticker->selected)
- {
- size_t len = utf8cpy(ticker->s,
- PATH_MAX_LENGTH, ticker->str, ticker->len - 3);
- ticker->s[len ] = '.';
- ticker->s[len+1] = '.';
- ticker->s[len+2] = '.';
- ticker->s[len+3] = '\0';
- return false;
- }
- /* Note: If we reach this point then str_len > ticker->len
- * (previously had an unecessary 'if (str_len > ticker->len)'
- * check here...) */
- switch (ticker->type_enum)
- {
- case TICKER_TYPE_LOOP:
- {
- size_t offset1, offset2, offset3;
- size_t width1, width2, width3;
- gfx_animation_ticker_loop(
- ticker->idx,
- ticker->len,
- str_len, utf8len(ticker->spacer),
- &offset1, &width1,
- &offset2, &width2,
- &offset3, &width3);
- build_ticker_loop_string(
- ticker->str, ticker->spacer,
- offset1, width1,
- offset2, width2,
- offset3, width3,
- ticker->s, PATH_MAX_LENGTH);
- }
- break;
- case TICKER_TYPE_BOUNCE:
- default:
- {
- size_t offset = gfx_animation_ticker_generic(
- ticker->idx,
- str_len - ticker->len);
- str_len = ticker->len;
- utf8cpy(
- ticker->s,
- PATH_MAX_LENGTH,
- utf8skip(ticker->str, offset),
- str_len);
- }
- break;
- }
- p_anim->flags |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
- return true;
- }
- /* 'Fixed width' font version of gfx_animation_ticker_smooth() */
- static bool gfx_animation_ticker_smooth_fw(
- gfx_animation_t *p_anim,
- gfx_animation_ctx_ticker_smooth_t *ticker)
- {
- size_t spacer_len = 0;
- unsigned glyph_width = ticker->glyph_width;
- unsigned src_str_width = 0;
- unsigned spacer_width = 0;
- bool success = false;
- bool is_active = false;
- /* Sanity check has already been performed by
- * gfx_animation_ticker_smooth() - no need to
- * repeat */
- /* Get length + width of src string */
- size_t src_str_len = utf8len(ticker->src_str);
- if (src_str_len < 1)
- goto end;
- src_str_width = (unsigned)(src_str_len * glyph_width);
- /* If src string width is <= text field width, we
- * can just copy the entire string */
- if (src_str_width <= ticker->field_width)
- {
- utf8cpy(ticker->dst_str, ticker->dst_str_len,
- ticker->src_str, src_str_len);
- if (ticker->dst_str_width)
- *ticker->dst_str_width = src_str_width;
- *ticker->x_offset = 0;
- success = true;
- goto end;
- }
- /* If entry is not selected, just clip input string
- * and add '...' suffix */
- if (!ticker->selected)
- {
- size_t _len;
- unsigned num_chars = 0;
- unsigned suffix_len = 3;
- unsigned suffix_width = suffix_len * glyph_width;
- /* Sanity check */
- if (ticker->field_width < suffix_width)
- goto end;
- /* Determine number of characters to copy */
- num_chars = (ticker->field_width - suffix_width) / glyph_width;
- /* Copy string segment + add suffix */
- _len = utf8cpy(ticker->dst_str, ticker->dst_str_len, ticker->src_str, num_chars);
- ticker->dst_str[_len ] = '.';
- ticker->dst_str[_len+1] = '.';
- ticker->dst_str[_len+2] = '.';
- ticker->dst_str[_len+3] = '\0';
- if (ticker->dst_str_width)
- *ticker->dst_str_width = (num_chars * glyph_width) + suffix_width;
- *ticker->x_offset = 0;
- success = true;
- goto end;
- }
- /* If we get this far, then a scrolling animation
- * is required... */
- /* Use default spacer, if none is provided */
- if (!ticker->spacer)
- ticker->spacer = TICKER_SPACER_DEFAULT;
- /* Get length + width of spacer */
- if ((spacer_len = utf8len(ticker->spacer)) < 1)
- goto end;
- spacer_width = (unsigned)(spacer_len * glyph_width);
- /* Determine animation type */
- switch (ticker->type_enum)
- {
- case TICKER_TYPE_LOOP:
- {
- unsigned char_offset1 = 0;
- unsigned num_chars1 = 0;
- unsigned char_offset2 = 0;
- unsigned num_chars2 = 0;
- unsigned char_offset3 = 0;
- unsigned num_chars3 = 0;
- gfx_animation_ticker_smooth_loop_fw(
- ticker->idx,
- src_str_width, src_str_len, spacer_width, spacer_len,
- glyph_width, ticker->field_width,
- &char_offset1, &num_chars1,
- &char_offset2, &num_chars2,
- &char_offset3, &num_chars3,
- ticker->x_offset);
- build_ticker_loop_string(
- ticker->src_str, ticker->spacer,
- char_offset1, num_chars1,
- char_offset2, num_chars2,
- char_offset3, num_chars3,
- ticker->dst_str, ticker->dst_str_len);
- if (ticker->dst_str_width)
- *ticker->dst_str_width = (num_chars1 + num_chars2 + num_chars3) * glyph_width;
- break;
- }
- case TICKER_TYPE_BOUNCE:
- default:
- {
- unsigned char_offset = 0;
- unsigned num_chars = 0;
- gfx_animation_ticker_smooth_generic_fw(
- ticker->idx,
- src_str_width, src_str_len, glyph_width, ticker->field_width,
- &char_offset, &num_chars, ticker->x_offset);
- /* Copy required substring */
- if (num_chars > 0)
- utf8cpy(
- ticker->dst_str, ticker->dst_str_len,
- utf8skip(ticker->src_str, char_offset), num_chars);
- else
- ticker->dst_str[0] = '\0';
- if (ticker->dst_str_width)
- *ticker->dst_str_width = num_chars * glyph_width;
- break;
- }
- }
- success = true;
- is_active = true;
- p_anim->flags |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
- end:
- if (!success)
- {
- *ticker->x_offset = 0;
- if (ticker->dst_str_len > 0)
- ticker->dst_str[0] = '\0';
- }
- return is_active;
- }
- bool gfx_animation_ticker_smooth(gfx_animation_ctx_ticker_smooth_t *ticker)
- {
- size_t i;
- size_t src_str_len = 0;
- size_t spacer_len = 0;
- unsigned small_src_char_widths[64] = {0};
- unsigned src_str_width = 0;
- unsigned spacer_width = 0;
- unsigned *src_char_widths = NULL;
- unsigned *spacer_char_widths = NULL;
- const char *str_ptr = NULL;
- bool success = false;
- bool is_active = false;
- gfx_animation_t *p_anim = &anim_st;
- /* Sanity check */
- if (string_is_empty(ticker->src_str) ||
- (ticker->dst_str_len < 1) ||
- (ticker->field_width < 1) || (
- #ifdef HAVE_FONT
- !ticker->font &&
- #endif
- (ticker->glyph_width < 1))
- )
- goto end;
- /* If we are using a fixed width font (ticker->font == NULL),
- * switch to optimised code path */
- #ifdef HAVE_FONT
- if (!ticker->font)
- #endif
- return gfx_animation_ticker_smooth_fw(p_anim, ticker);
- #ifdef HAVE_FONT
- /* Find the display width of each character in
- * the src string + total width */
- if ((src_str_len = utf8len(ticker->src_str)) < 1)
- goto end;
- src_char_widths = small_src_char_widths;
- if (src_str_len > ARRAY_SIZE(small_src_char_widths))
- {
- if (!(src_char_widths = (unsigned*)calloc(src_str_len, sizeof(unsigned))))
- goto end;
- }
- str_ptr = ticker->src_str;
- for (i = 0; i < src_str_len; i++)
- {
- int glyph_width = font_driver_get_message_width(
- ticker->font, str_ptr, 1, ticker->font_scale);
- if (glyph_width < 0)
- goto end;
- src_char_widths[i] = (unsigned)glyph_width;
- src_str_width += (unsigned)glyph_width;
- str_ptr = utf8skip(str_ptr, 1);
- }
- /* If total src string width is <= text field width, we
- * can just copy the entire string */
- if (src_str_width <= ticker->field_width)
- {
- utf8cpy(ticker->dst_str, ticker->dst_str_len,
- ticker->src_str, src_str_len);
- if (ticker->dst_str_width)
- *ticker->dst_str_width = src_str_width;
- *ticker->x_offset = 0;
- success = true;
- goto end;
- }
- /* If entry is not selected, just clip input string
- * and add '...' suffix */
- if (!ticker->selected)
- {
- size_t _len;
- unsigned text_width;
- unsigned current_width = 0;
- unsigned num_chars = 0;
- int period_width =
- font_driver_get_message_width(ticker->font,
- ".", 1, ticker->font_scale);
- /* Sanity check */
- if (period_width < 0)
- goto end;
- if (ticker->field_width < (3 * (unsigned)period_width))
- goto end;
- /* Determine number of characters to copy */
- text_width = ticker->field_width - (3 * period_width);
- for (;;)
- {
- current_width += src_char_widths[num_chars];
- if (current_width > text_width)
- {
- /* Have to go back one in order to get 'actual'
- * value for dst_str_width */
- current_width -= src_char_widths[num_chars];
- break;
- }
- num_chars++;
- }
- /* Copy string segment + add suffix */
- _len = utf8cpy(ticker->dst_str, ticker->dst_str_len,
- ticker->src_str, num_chars);
- ticker->dst_str[_len ] = '.';
- ticker->dst_str[_len+1] = '.';
- ticker->dst_str[_len+2] = '.';
- ticker->dst_str[_len+3] = '\0';
- if (ticker->dst_str_width)
- *ticker->dst_str_width = current_width + (3 * period_width);
- *ticker->x_offset = 0;
- success = true;
- goto end;
- }
- /* If we get this far, then a scrolling animation
- * is required... */
- /* Use default spacer, if none is provided */
- if (!ticker->spacer)
- ticker->spacer = TICKER_SPACER_DEFAULT;
- /* Find the display width of each character in
- * the spacer */
- if ((spacer_len = utf8len(ticker->spacer)) < 1)
- goto end;
- if (!(spacer_char_widths = (unsigned*)calloc(spacer_len, sizeof(unsigned))))
- goto end;
- str_ptr = ticker->spacer;
- for (i = 0; i < spacer_len; i++)
- {
- int glyph_width = font_driver_get_message_width(
- ticker->font, str_ptr, 1, ticker->font_scale);
- if (glyph_width < 0)
- goto end;
- spacer_char_widths[i] = (unsigned)glyph_width;
- spacer_width += (unsigned)glyph_width;
- str_ptr = utf8skip(str_ptr, 1);
- }
- /* Determine animation type */
- switch (ticker->type_enum)
- {
- case TICKER_TYPE_LOOP:
- {
- unsigned char_offset1 = 0;
- unsigned num_chars1 = 0;
- unsigned char_offset2 = 0;
- unsigned num_chars2 = 0;
- unsigned char_offset3 = 0;
- unsigned num_chars3 = 0;
- gfx_animation_ticker_smooth_loop(
- ticker->idx,
- src_char_widths, src_str_len,
- spacer_char_widths, spacer_len,
- src_str_width, spacer_width, ticker->field_width,
- &char_offset1, &num_chars1,
- &char_offset2, &num_chars2,
- &char_offset3, &num_chars3,
- ticker->x_offset, ticker->dst_str_width);
- build_ticker_loop_string(
- ticker->src_str, ticker->spacer,
- char_offset1, num_chars1,
- char_offset2, num_chars2,
- char_offset3, num_chars3,
- ticker->dst_str, ticker->dst_str_len);
- break;
- }
- case TICKER_TYPE_BOUNCE:
- default:
- {
- unsigned char_offset = 0;
- unsigned num_chars = 0;
- gfx_animation_ticker_smooth_generic(
- ticker->idx,
- src_char_widths, src_str_len,
- src_str_width, ticker->field_width,
- &char_offset, &num_chars,
- ticker->x_offset, ticker->dst_str_width);
- /* Copy required substring */
- if (num_chars > 0)
- utf8cpy(
- ticker->dst_str, ticker->dst_str_len,
- utf8skip(ticker->src_str, char_offset), num_chars);
- else
- ticker->dst_str[0] = '\0';
- break;
- }
- }
- success = true;
- is_active = true;
- p_anim->flags |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
- #endif
- end:
- if (src_char_widths != small_src_char_widths && src_char_widths)
- {
- free(src_char_widths);
- src_char_widths = NULL;
- }
- if (spacer_char_widths)
- {
- free(spacer_char_widths);
- spacer_char_widths = NULL;
- }
- if (!success)
- {
- *ticker->x_offset = 0;
- if (ticker->dst_str_len > 0)
- ticker->dst_str[0] = '\0';
- }
- return is_active;
- }
- bool gfx_animation_line_ticker(gfx_animation_ctx_line_ticker_t *line_ticker)
- {
- char *wrapped_str = NULL;
- size_t wrapped_str_len = 0;
- size_t line_ticker_str_len = 0;
- struct string_list lines = {0};
- size_t line_offset = 0;
- bool success = false;
- bool is_active = false;
- gfx_animation_t *p_anim = &anim_st;
- /* Sanity check */
- if (!line_ticker)
- return false;
- if (string_is_empty(line_ticker->str) ||
- (line_ticker->line_len < 1) ||
- (line_ticker->max_lines < 1))
- goto end;
- /* Line wrap input string */
- line_ticker_str_len = strlen(line_ticker->str);
- wrapped_str_len = line_ticker_str_len + 1 + 10; /* 10 bytes use for inserting '\n' */
- if (!(wrapped_str = (char*)malloc(wrapped_str_len)))
- goto end;
- wrapped_str[0] = '\0';
- word_wrap(
- wrapped_str,
- wrapped_str_len,
- line_ticker->str,
- line_ticker_str_len,
- (int)line_ticker->line_len,
- 100, 0);
- if (string_is_empty(wrapped_str))
- goto end;
- /* Split into component lines */
- string_list_initialize(&lines);
- if (!string_split_noalloc(&lines, wrapped_str, "\n"))
- goto end;
- /* Check whether total number of lines fits within
- * the set limit */
- if (lines.size <= line_ticker->max_lines)
- {
- strlcpy(line_ticker->s, wrapped_str, line_ticker->len);
- success = true;
- goto end;
- }
- /* Determine offset of first line in wrapped string */
- switch (line_ticker->type_enum)
- {
- case TICKER_TYPE_LOOP:
- line_offset = gfx_animation_line_ticker_loop(
- line_ticker->idx,
- line_ticker->line_len,
- lines.size);
- break;
- case TICKER_TYPE_BOUNCE:
- default:
- line_offset = gfx_animation_line_ticker_generic(
- line_ticker->idx,
- line_ticker->line_len,
- line_ticker->max_lines,
- lines.size);
- break;
- }
- /* Build output string from required lines */
- build_line_ticker_string(
- line_ticker->max_lines, line_offset, &lines,
- line_ticker->s, line_ticker->len);
- success = true;
- is_active = true;
- p_anim->flags |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
- end:
- if (wrapped_str)
- {
- free(wrapped_str);
- wrapped_str = NULL;
- }
- string_list_deinitialize(&lines);
- if (!success)
- if (line_ticker->len > 0)
- line_ticker->s[0] = '\0';
- return is_active;
- }
- bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *line_ticker)
- {
- char *wrapped_str = NULL;
- size_t line_ticker_src_len = 0;
- size_t wrapped_str_len = 0;
- struct string_list lines = {0};
- int glyph_width = 0;
- int glyph_height = 0;
- size_t line_len = 0;
- size_t max_display_lines = 0;
- size_t num_display_lines = 0;
- size_t line_offset = 0;
- size_t top_fade_line_offset = 0;
- size_t bottom_fade_line_offset = 0;
- bool fade_active = false;
- bool success = false;
- bool is_active = false;
- gfx_animation_t *p_anim = &anim_st;
- const char *wideglyph_str = NULL;
- int wideglyph_width = 100;
- void (*word_wrap_func)(char *dst, size_t dst_size,
- const char *src, size_t src_len,
- int line_width, int wideglyph_width, unsigned max_lines);
- /* Sanity check */
- if (!line_ticker)
- return false;
- #ifdef HAVE_FONT
- if (!line_ticker->font ||
- string_is_empty(line_ticker->src_str) ||
- (line_ticker->field_width < 1) ||
- (line_ticker->field_height < 1))
- goto end;
- /* Get font dimensions */
- /* > Width
- * This is a bit of a fudge. Performing a 'font aware'
- * (i.e. character display width) word wrap is too CPU
- * intensive, so we just sample the width of a common
- * character and hope for the best. (We choose 'a' because
- * this is what Ozone uses for spacing calculations, and
- * it is proven to work quite well) */
- if ((glyph_width = font_driver_get_message_width(
- line_ticker->font, "a", 1, line_ticker->font_scale)) <= 0)
- goto end;
- if ((wideglyph_str = msg_hash_get_wideglyph_str()))
- {
- int new_glyph_width = font_driver_get_message_width(
- line_ticker->font, wideglyph_str, strlen(wideglyph_str),
- line_ticker->font_scale);
-
- if (new_glyph_width > 0)
- wideglyph_width = new_glyph_width * 100 / glyph_width;
- word_wrap_func = word_wrap_wideglyph;
- }
- else
- word_wrap_func = word_wrap;
- /* > Height */
- if ((glyph_height = font_driver_get_line_height(
- line_ticker->font, line_ticker->font_scale)) <= 0)
- goto end;
- /* Determine line wrap parameters */
- line_len = (size_t)(line_ticker->field_width / glyph_width);
- max_display_lines = (size_t)(line_ticker->field_height / glyph_height);
- if ((line_len < 1) || (max_display_lines < 1))
- goto end;
- /* Line wrap input string */
- line_ticker_src_len = strlen(line_ticker->src_str);
- /* 10 bytes use for inserting '\n' */
- wrapped_str_len = line_ticker_src_len + 1 + 10;
- if (!(wrapped_str = (char*)malloc(wrapped_str_len)))
- goto end;
- wrapped_str[0] = '\0';
- (word_wrap_func)(
- wrapped_str,
- wrapped_str_len,
- line_ticker->src_str,
- line_ticker_src_len,
- (int)line_len,
- wideglyph_width, 0);
- if (string_is_empty(wrapped_str))
- goto end;
- string_list_initialize(&lines);
- /* Split into component lines */
- if (!string_split_noalloc(&lines, wrapped_str, "\n"))
- goto end;
- /* Check whether total number of lines fits within
- * the set field limit */
- if (lines.size <= max_display_lines)
- {
- strlcpy(line_ticker->dst_str, wrapped_str, line_ticker->dst_str_len);
- *line_ticker->y_offset = 0.0f;
- /* No fade animation is required */
- if (line_ticker->fade_enabled)
- {
- if (line_ticker->top_fade_str_len > 0)
- line_ticker->top_fade_str[0] = '\0';
- if (line_ticker->bottom_fade_str_len > 0)
- line_ticker->bottom_fade_str[0] = '\0';
- *line_ticker->top_fade_y_offset = 0.0f;
- *line_ticker->bottom_fade_y_offset = 0.0f;
- *line_ticker->top_fade_alpha = 0.0f;
- *line_ticker->bottom_fade_alpha = 0.0f;
- }
- success = true;
- goto end;
- }
- /* Determine which lines should be shown, along with
- * y axis draw offset */
- switch (line_ticker->type_enum)
- {
- case TICKER_TYPE_LOOP:
- gfx_animation_line_ticker_smooth_loop(
- line_ticker->idx,
- line_ticker->fade_enabled,
- line_len, (size_t)glyph_height,
- max_display_lines, lines.size,
- &num_display_lines, &line_offset, line_ticker->y_offset,
- &fade_active,
- &top_fade_line_offset, line_ticker->top_fade_y_offset, line_ticker->top_fade_alpha,
- &bottom_fade_line_offset, line_ticker->bottom_fade_y_offset, line_ticker->bottom_fade_alpha);
- break;
- case TICKER_TYPE_BOUNCE:
- default:
- gfx_animation_line_ticker_smooth_generic(
- line_ticker->idx,
- line_ticker->fade_enabled,
- line_len, (size_t)glyph_height,
- max_display_lines, lines.size,
- &num_display_lines, &line_offset, line_ticker->y_offset,
- &fade_active,
- &top_fade_line_offset, line_ticker->top_fade_y_offset, line_ticker->top_fade_alpha,
- &bottom_fade_line_offset, line_ticker->bottom_fade_y_offset, line_ticker->bottom_fade_alpha);
- break;
- }
- /* Build output string from required lines */
- build_line_ticker_string(
- num_display_lines, line_offset, &lines,
- line_ticker->dst_str, line_ticker->dst_str_len);
- /* Extract top/bottom fade strings, if required */
- if (fade_active)
- {
- /* We waste a handful of clock cycles by using
- * build_line_ticker_string() here, but it saves
- * rewriting a heap of code... */
- build_line_ticker_string(
- 1, top_fade_line_offset, &lines,
- line_ticker->top_fade_str, line_ticker->top_fade_str_len);
- build_line_ticker_string(
- 1, bottom_fade_line_offset, &lines,
- line_ticker->bottom_fade_str, line_ticker->bottom_fade_str_len);
- }
- success = true;
- is_active = true;
- p_anim->flags |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
- end:
- #endif
- if (wrapped_str)
- {
- free(wrapped_str);
- wrapped_str = NULL;
- }
- string_list_deinitialize(&lines);
- if (!success)
- {
- if (line_ticker->dst_str_len > 0)
- line_ticker->dst_str[0] = '\0';
- if (line_ticker->fade_enabled)
- {
- if (line_ticker->top_fade_str_len > 0)
- line_ticker->top_fade_str[0] = '\0';
- if (line_ticker->bottom_fade_str_len > 0)
- line_ticker->bottom_fade_str[0] = '\0';
- *line_ticker->top_fade_alpha = 0.0f;
- *line_ticker->bottom_fade_alpha = 0.0f;
- }
- }
- return is_active;
- }
- bool gfx_animation_kill_by_tag(uintptr_t *tag)
- {
- unsigned i;
- gfx_animation_t *p_anim = &anim_st;
- if (!tag || *tag == (uintptr_t)-1)
- return false;
- /* Scan animation list */
- for (i = 0; i < RBUF_LEN(p_anim->list); ++i)
- {
- struct tween *t = &p_anim->list[i];
- if (t->tag != *tag)
- continue;
- /* If we are currently inside gfx_animation_update(),
- * we are already looping over p_anim->list entries
- * > Cannot modify p_anim->list now, so schedule a
- * delete for when the gfx_animation_update() loop
- * is complete */
- if (p_anim->flags & GFX_ANIM_FLAG_IN_UPDATE)
- {
- t->deleted = true;
- p_anim->flags |= GFX_ANIM_FLAG_PENDING_DELETES;
- }
- else
- {
- RBUF_REMOVE(p_anim->list, i);
- --i;
- }
- }
- /* If we are currently inside gfx_animation_update(),
- * also have to scan *pending* animation list
- * (otherwise any entries that are simultaneously added
- * and deleted inside gfx_animation_update() won't get
- * deleted at all, producing utter chaos) */
- if (p_anim->flags & GFX_ANIM_FLAG_IN_UPDATE)
- {
- for (i = 0; i < RBUF_LEN(p_anim->pending); ++i)
- {
- struct tween *t = &p_anim->pending[i];
- if (t->tag != *tag)
- continue;
- RBUF_REMOVE(p_anim->pending, i);
- --i;
- }
- }
- return true;
- }
- void gfx_animation_deinit(void)
- {
- gfx_animation_t *p_anim = &anim_st;
- if (!p_anim)
- return;
- RBUF_FREE(p_anim->list);
- RBUF_FREE(p_anim->pending);
- if (p_anim->updatetime_cb)
- p_anim->updatetime_cb = NULL;
- memset(p_anim, 0, sizeof(*p_anim));
- }
- void gfx_animation_timer_start(float *timer, gfx_timer_ctx_entry_t *timer_entry)
- {
- gfx_animation_ctx_entry_t entry;
- uintptr_t tag = (uintptr_t) timer;
- gfx_animation_kill_by_tag(&tag);
- *timer = 0.0f;
- entry.easing_enum = EASING_LINEAR;
- entry.tag = tag;
- entry.duration = timer_entry->duration;
- entry.target_value = 1.0f;
- entry.subject = timer;
- entry.cb = timer_entry->cb;
- entry.userdata = timer_entry->userdata;
- gfx_animation_push(&entry);
- }
|