На самом деле, результат работы всех этих im-функций - строки, а не реальные изображения, поэтому работа со всем этим довольно быстра.

Также существует кэширование, поэтому если вы вдруг захотите нарисовать 1000 объектов в одной текстуре,
то это будет сделано только один раз и только во время первой отрисовки,
по крайней мере до тех пор, пока вы не измените хоть одну координату любого объекта и не попытаетесь отрисовать эту текстуру ещё раз.

Следует отличать команду показать картинку в последующих кадрах (`show`) и реальную отрисовку (`image` в ScreenLang).
Т. к. если вы добавите изображение в список отображения и тут же удалите, то часть движка,
ответственная за текстуры, даже не узнает о том, что что-то изменилось.

***

Для примера рассмотрим упрощённый процесс отрисовки 2 изображений.

Это именно *упрощённый* разбор того, что когда-то происходило "под капотом".
Сейчас некоторые детали сделаны чуть иначе, но общие черты остались прежними.
Разумеется, вы можете посмотреть, как работают im-функции, и вместо их вызова сразу составлять нужные строки,
но рекомендуется так НЕ делать, т. к. внутренний формат в любой момент может быть изменён.

Пусть у нас есть:  
```renpy
image bus_stop_left  = im.crop(im.scale("images/bg/bus_stop.jpg", 64, 45), ( 0, 0, 32, 45))
image bus_stop_right = im.crop(im.scale("images/bg/bus_stop.jpg", 64, 45), (32, 0, 32, 45))
```

Т. к. im-функции возвращают строки, то это примерно то же самое, что и
```renpy
image bus_stop_left  = "crop (scale (images/bg/bus_stop.jpg) 64 45) ( 0 0 32 45)"
image bus_stop_right = "crop (scale (images/bg/bus_stop.jpg) 64 45) (32 0 32 45)"
```

Теперь происходит попытка отрисовать всё в первый раз:

При отрисовке изображения `bus_stop_left`:
* Запрашивается текстура `"crop (scale (images/bg/bus_stop.jpg) 64 45) (0 0 32 45)"`.
	* Вызывается `crop` с параметрами `(scale (images/bg/bus_stop.jpg) 64 45)` и `(0 0 32 45)`.
		* Запрашивается текстура `"scale (images/bg/bus_stop.jpg) 64 45"`.
			* Вызывается `scale` с параметрами `images/bg/bus_stop.jpg`, `64` и `45`.
				* Запрашивается текстура `"images/bg/bus_stop.jpg"`.
					* Загружается файл `"images/bg/bus_stop.jpg"`.
						* Результат сохраняется в кэше и возвращается.
				* Масштабируется к размерам 64х45.
				* Результат сохраняется в кэше и возвращается.
		* Делается вырезка текстуры 32х45 из координат (0, 0).
		* Результат сохраняется в кэше и возвращается.
* Готово.

При отрисовке изображения `bus_stop_right`:
* Запрашивается текстура `"crop (scale (images/bg/bus_stop.jpg) 64 45) (32 0 32 45)"`.
	* Вызывается `crop` с параметрами `(scale (images/bg/bus_stop.jpg) 64 45)` и `(32 0 32 45)`.
		* Запрашивается текстура `"scale (images/bg/bus_stop.jpg) 64 45"`.
		* Такая текстура уже есть в кэше, поэтому она сразу берётся оттуда.
		* Делается вырезка текстуры 32х45 из координат (32, 0).
		* Результат сохраняется в кэше и возвращается.
* Готово.

При повторной отрисовке:

Для `bus_stop_left`:
* Запрашивается текстура `"crop (scale (images/bg/bus_stop.jpg) 64 45) (0 0 32 45)"`.
* Такая текстура уже есть в кэше, поэтому она сразу берётся оттуда.
* Готово.
	
Аналогично для `bus_stop_right`.

***

UPD:  
Как и говорилось выше, формат может быть изменён, что и произошло.  
Пробелы между аргументами команд были заменены на символ `|` (вертикальная черта).  
Это было сделано для того, чтобы пробелы стали доступны внутри пути к изображениям.  
Возможно, в будущем похожие вещи будут происходить ещё не раз, и
именно поэтому настоятельно рекомендуется использовать im-функции и никогда не составлять эти строки вручную.

***

Текстуры - это окончательные изображения, которые выводятся на экран, а до всего этого идёт работа с поверхностями (surface), и описанное выше относится именно к ним.  
Когда требуется текстура, проверяется её наличие в кэше текстур, если оно есть - оно возвращается.
Если же его нет:
* Оно запрашивается из кэша поверхностей.
* При запросе оно либо находится, либо создаётся там.
* Из найденной или созданной поверхности создаётся текстура и она возвращается.

К каждой текстуре привязана поверхность, из которой эта текстура создана, и поверхность не может быть удалена, пока используется текстура из неё.

Но кэши не бесконечны, ясное дело (иначе ваша игра сожрала бы всю оперативку, и начались бы ужасные лаги из-за swap'а).
Их размер ограничен значениями в файле `resources/params.conf`.  
Если какой-либо из кэшей начинает занимать больше места, чем ему разрешено, то удаляются те изображения, которые запрашивались как можно более длительный срок назад.  
Если все изображения нужны для отрисовки текущего кадра, то ни одно из них не удаляется, и кэши продолжают занимать больше положенного.

***

Кстати, если нужно загрузить изображение, которое уже загружалось,
но после этого было изменено, то можно добавить после пути к нему `"?some_text"`.  
Это `"?some_text"` считается за часть имени текстуры, но отбрасывается непосредственно при загрузке файла.

Например, вы можете использовать изображение  
`"images/some_image.png?" + str(os.path.getmtime("images/some_image.png"))`  
В этом случае, если изображение `"images/some_image.png"` было изменено после того, как было загружено, то оно автоматически перезагрузится.

Это произойдёт из-за того, что `os.path.getmtime` возвращает время изменения файла,
т. е. после изменения изображения будет запрашиваться уже другое изображение с тем же путём (путь указывается до знака `?`).

Такой приём используется в скринах сохранения и загрузки,
т. к. при перезаписи уже существующего сохранения скриншот старого сохранения становится неактуальным
и нужна только его последняя версия.

***

Разумеется, чтобы просто брать и пользоваться движком, знать всё это не обязательно.  
Но понимание устройства кэша и создания текстур поможет вам написать более быструю и производительную, а также менее требовательную игру.
