Java/Processing - масштабировать матрицу изображения с оберточными краями
Мне было интересно, как эффективно реализовать следующую процедуру масштабирования изображения в Java или Processing. При масштабировании границы изображения оборачиваются по краям экрана. Я хочу применить то же самое во время выполнения к массиву Pixels() в программе Processing. (чтобы сохранить эту независимость от обработки - Pixels() - это не что иное, как метод, который возвращает все пиксели на моем текущем экране в массиве).
(Обратите внимание, что этот пример был сделан в MaxMsp/Jitter с использованием модуля jit.rota, который, похоже, использует очень эффективную реализацию).
Может кто-нибудь помочь мне с тем, как начать? Я предполагаю, что это должно быть сочетание уменьшения масштаба изображения и создания его вспомогательных копий, но для меня это звучит не очень эффективно. Приведенный выше пример отлично работает с видео даже с самыми экстремальными настройками.
2 ответа
Один вариант, который я думаю, будет быстрым, это использование базового фрагментного шейдера.
К счастью, у вас есть пример, довольно близкий к тому, что вам нужно, который поставляется с Обработка через Файл> Примеры> Темы> Шейдеры> Бесконечные плитки
Я не смогу эффективно предоставить достойное руководство для начала и конца, но на веб-сайте Processing есть исчерпывающее руководство по PShader, если вы начинаете с нуля.
Действительно грубая суть того, что вам нужно:
- шейдеры - это программы, которые работают очень быстро и распараллелены на GPU, разделены на две части: вершинные шейдеры (в основном, с трехмерной геометрией), фрагментные шейдеры (в основном, с "фрагментами"(которые собираются стать пикселями на экране)). Вы захотите поиграть с фрагментным шейдером
- Язык называется GLSL и немного отличается (меньше типов, более строгий, более простой синтаксис), но не является абсолютно чуждым (похожий тип объявления переменных C, функций, условий, циклов и т. Д.)
- если вы хотите сделать переменную из GLSL-программы доступной в Processing, вы ставите ее перед ключевым словом
uniform
- используйте textureWrap(REPEAT), чтобы обернуть края
- чтобы масштабировать изображение и обернуть его, вам нужно масштабировать координаты выборки текстуры:
Вот как выглядит шейдерный скроллер Infinite Tiles:
//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------
uniform float time;
uniform vec2 resolution;
uniform sampler2D tileImage;
#define TILES_COUNT_X 4.0
void main() {
vec2 pos = gl_FragCoord.xy - vec2(4.0 * time);
vec2 p = (resolution - TILES_COUNT_X * pos) / resolution.x;
vec3 col = texture2D (tileImage, p).xyz;
gl_FragColor = vec4 (col, 1.0);
}
Вы можете немного упростить это, поскольку вам не нужно прокручивать. Кроме того, вместо вычитания и умножения (- TILES_COUNT_X * pos
), вы можете просто умножить:
//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------
uniform float scale;
uniform vec2 resolution;
uniform sampler2D tileImage;
void main() {
vec2 pos = gl_FragCoord.xy * vec2(scale);
vec2 p = (resolution - pos) / resolution.x;
vec3 col = texture2D (tileImage, p).xyz;
gl_FragColor = vec4 (col, 1.0);
}
Обратите внимание, я изменил time
переменная, чтобы стать scale
поэтому код обработки, обращающийся к этой единой переменной, также должен измениться:
//-------------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//-------------------------------------------------------------
PImage tileTexture;
PShader tileShader;
void setup() {
size(640, 480, P2D);
textureWrap(REPEAT);
tileTexture = loadImage("penrose.jpg");
loadTileShader();
}
void loadTileShader() {
tileShader = loadShader("scroller.glsl");
tileShader.set("resolution", float(width), float(height));
tileShader.set("tileImage", tileTexture);
}
void draw() {
tileShader.set("scale", map(mouseX,0,width,-3.0,3.0));
shader(tileShader);
rect(0, 0, width, height);
}
Переместите мышь, чтобы изменить масштаб:
Обновление Вы можете поиграть с очень похожим шейдером здесь:
Я действительно придумал решение - но затем реализую метод Джорджа, поскольку разница в скорости с использованием шейдеров, кажется, того стоит!
public void scalePixels(double wRatio,double hRatio, PGraphics viewPort) {
viewPort.loadPixels();
int[] PixelsArrayNew = viewPort.pixels.clone();
double x_ratio = wRatio ;
double y_ratio = hRatio ;
double px, py ;
for (int i=0;i<viewPort.height;i++) {
for (int j=0;j<viewPort.width;j++) {
px = Math.floor(j%(wRatio*viewPort.width)/x_ratio) ;
py = Math.floor(i%(hRatio*viewPort.height)/y_ratio) ;
viewPort.pixels[(int)(i*viewPort.width)+j] = PixelsArrayNew[(int)((py*viewPort.width)+px)] ;
}
}
viewPort.updatePixels();
}