Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Как получить адреса пикселей BitmapData? (http://www.flasher.ru/forum/showthread.php?t=162331)

Gantenbain 31.07.2011 19:44

Как получить адреса пикселей BitmapData?
 
Всем - добрый день!

Вопрос уже задавал на другом форуме, однако специализация и представительность этого форума внушают мне столь обширные надежды, что можно и нескромно повториться.
Задача - получить адреса пикселей BitmapData до помещения связанного объекта Bitmap в список отображения (для реализации специфической заливки отображаемой фигуры, созданной средствами векторной графики).
Однако, единственный способ, который пришел мне в голову, не работает:

Код:

var tmpSh:Shape = new Shape();
tmpSh.graphics.beginFill(0xFF00FF, 1);
tmpSh.graphics.drawPath(commands, data);
tmpSh.graphics.endFill();
var r:Rectangle = tmpSh.getRect(stage);
var pt:Point = r.topLeft;
var pixelValue:uint, pixelValueMask:uint, i:int = 0, j:int = 0;
var bmpdata:BitmapData = new BitmapData(r.width, r.height, false, 0xFF0000);
bmpdata.draw(tmpSh);
var arr:Vector.<String> = new Vector.<String>();
for (i = 0; i < r.width-1; i++) {
  for (j = 0; j < r.height-1; j++) {
    pixelValue =  bmpdata.getPixel(i, j);
    if (arr.length == 0 || arr.indexOf(("0x"+pixelValue.toString(16))) < 0)  arr.push("0x"+ pixelValue.toString(16));
  }
}
trace(arr);

В массиве присутствуют значения цвета лишь 0xFF0000 (фигура, заданная drawPath(commands, data), естественно, отрисовывается правильно после добавления ее в контейнер отображения). Есть ли какой-либо способ достать ее пиксели не отображая ее?

Wolsh 31.07.2011 21:32

Код AS3:

var tmpSh:Shape = new Shape();
tmpSh.graphics.beginFill(0xFF00FF, 1);
tmpSh.graphics.drawRect(5, 5, 20, 20);// drawPath(commands, data);
tmpSh.graphics.endFill();
var pixelValue:uint, pixelValueMask:uint, i:int = 0, j:int = 0;
var bmpdata:BitmapData = new BitmapData(tmpSh.width, tmpSh.height, false, 0xFF0000);
bmpdata.draw(tmpSh);
var arr:Vector.<String> = new Vector.<String>();
for (i = 0; i < tmpSh.width-1; i++) {
        for (j = 0; j < tmpSh.height-1; j++) {
                pixelValue =  bmpdata.getPixel(i, j);
                if (arr.length == 0 || arr.indexOf(("0x"+pixelValue.toString(16))) < 0)  arr.push("0x"+ pixelValue.toString(16));
        }
}
trace(arr); // 0xff0000,0xff00ff


Gantenbain 01.08.2011 09:40

Спасибо, за оперативный ответ.
Думаю, Вас бы очень повеселило выражение моего лица, с которым смотрю на
Цитата:

// 0xff0000,0xff00ff
Возможно, что проблема связана с тем, что "заливку" "большой" Bitmap провожу в несколько этапов, с помощью "внутренней" функции, в качестве параметра которой передаю this
Код:

var paintSh:Function = function(bmp:Bitmap):void {
Однако, некоторый толчок мысли задан...
P.S. "Внутренняя" функция ни при чем - квадрат возвращает 2 цвета...Что-то возле drawPath...
P.P.S. Если определяю параметры commands, data внутри "внутренней" функции, то нахожу 2 цвета в bitmapData, при попытке передать параметры извне - 1 цвет.

Добавлено через 1 час 43 минуты
Все понял. paintSh(this) в моей реализации передает ссылку на глобальный объект вместо экземпляра класса. Досадный ляп. Большое спасибо, Wolsh.
P.S. И это не то :). Так не долго в рассудке повредиться

Добавлено через 3 часа 57 минут
Несмотря на то, что, благодаря Wolsh, доступ к адресам пикселей оказался открыт :), академический интерес к проблеме остался (пример сугубо условный):
Код AS3:

package game
{
        import flash.display.Sprite;
 
        public class  Example extends Sprite
        {
                public function Example() {
                        var er:MyClass = new MyClass();
                        addChild(er);
                }
        }
 
}

Код AS3:

package game
{
        import flash.display.Bitmap;
        import flash.display.BitmapData;
        import flash.display.Shape;
        import flash.geom.Point;
        import flash.geom.Rectangle;
 
        public class MyClass extends Bitmap
        {
                private var commands:Vector.<int>;
                private var data:Vector.<Number>;
 
                public function MyClass() {
                        var thisbitmapdata:BitmapData = new BitmapData(800, 600, true, 0x80FFFFFF);
                        this.bitmapData = thisbitmapdata;
                        commands = Vector.<int>([1, 2, 2, 2, 2]);
                        data = Vector.<Number>([5, 5, 20, 5, 20, 25, 5, 25, 5, 5]);
                        var i:int;
                        var paintBm:Function = function(bmp:Bitmap):void {
                                trace(bmp);
                                var tmpSh:Shape = new Shape();
                                tmpSh.graphics.beginFill(0xFF00FF, 1);
                                tmpSh.graphics.drawPath(commands, data);
                                //tmpSh.graphics.drawPath(Vector.<int>([1, 2, 2, 2, 2]), Vector.<Number>([5, 5, 20, 5, 20, 25, 5, 25, 5, 5]));
                                tmpSh.graphics.endFill();
                                var r:Rectangle = tmpSh.getRect(stage);
                                var pixelValue:uint;
                                var arr:Vector.<String> = new Vector.<String>();
                                var bmpdata:BitmapData = new BitmapData(r.width, r.height, false, 0xFF0000);
                                bmpdata.draw(tmpSh);
                                for (var m:int = 0; m < r.width-1; m++) {
                                        for (var j:int = 0; j < r.height-1; j++) {
                                                pixelValue =  bmpdata.getPixel(m, j);
                                        if (arr.length == 0 || arr.indexOf(("0x"+pixelValue.toString(16))) < 0)  arr.push("0x"+ pixelValue.toString(16));
                                        }
                                }
                                trace(arr);
                                bmp.bitmapData.draw(tmpSh);
                        }
                        while(i < 660) {
                        for each (var e:Number in data) data[data.indexOf(e)] = e + i;
                        paintBm(this);
                        i += 50;
                        }
                }
        }
 
}

Перекинем комментарий с одного "tmpSh.graphics.drawPath(" на другой и видим 2 цвета, вместо одного (?). Попытки передать в функцию параметры, или, создав новые массивы внутри функции, скопировать в них данные из внешних массивов - тупиковые.

Добавлено через 7 часов 3 минуты
Ну и теперь совсем лишившее меня каких-либо идей обстоятельство: при попытке проследить связи объектов теми средствами, что дает FlashDeveloper (точки останова ставил на <bmpdata.draw(tmpSh);> и <paintBm(this);>), обнаружил изменение в поведении программы - теперь выводятся все пикселы массива..., т.е. пошаговое действие отличается от нормальной работы. Внятного объяснения не имею.

Добавлено через 7 часов 38 минут
В общем, размещение
Код AS3:

var tmpSh:Shape = new Shape();

среди полей класса, а не во внутренней функции, решает проблему. Тему можно закрыть.

Wolsh 01.08.2011 18:03

В 10 плеере у БитмапДаты появился метод getVector, возвращающий Вектор с ARGB-значениями всех пикселей заданного прямоугольника битмапдаты. Например
Код AS3:

......
var tmpSh:Shape = new Shape();
tmpSh.graphics.beginFill(0xFF00FF, 1);
tmpSh.graphics.drawRect(5, 5, 20, 20);// drawPath(commands, data);
tmpSh.graphics.endFill();
var pixelValue:uint, pixelValueMask:uint, i:int = 0, j:int = 0;
var bmpdata:BitmapData = new BitmapData(tmpSh.width, tmpSh.height, false, 0xFF0000);
bmpdata.draw(tmpSh);
var pixels:Vector.<uint> = bmpdata.getVector(bmpdata.rect);
var palette:Vector.<uint> = pixels.sort(descSort).filter(uniqueSort);
trace( vectorColorsToString(palette, 6, "#") );
}
 
private function uniqueSort(item:uint, index:int, vector:Vector.<uint>):Boolean
{
        if (index === 0) return true;
        return (item !== vector[index - 1]);
}
 
private function descSort(x:uint, y:uint):Number
{
        if (x < y) return 1;
        if (x > y) return -1;
        return 0;
}
 
private function vectorColorsToString(vector:Vector.<uint>, digits:uint = 6, prefix:String = "0x"):Vector.<String>
{
        var colors:Vector.<String> = new Vector.<String>();
        for (var i:uint = 0; i < vector.length; i++)
        {
                var color:String = vector[i].toString(16);
                color = "00000000" + color;
                color = color.substr(color.length - digits, digits);
                colors.push(prefix + color);
        }
        return colors;
}


Gantenbain 01.08.2011 21:40

Да, я пробовал и этим вариантом снимать данные с BitmapData...
Кстати, должен покаяться (как бы ни было досадно это делать) - поторопился отрапортовать, что удовлетворил свой "академический интерес" :(... Давно не ощущал себя в такой прострации:
чтобы не гонять бессмысленные циклы, изменим
Код AS3:

while(i < 150)

, добавим переменную
Код AS3:

var trigg:int = 0;

и внутрь var paintBm:
Код AS3:

trigg = 1 - trigg;
var vvv:Vector.<Vector.<Number>> = Vector.<Vector.<Number>>([Vector.<Number>([5, 5, 20, 5, 20, 25, 5, 25, 5, 5]), Vector.<Number>([15, 15, 30, 15, 30, 35, 15, 35, 15, 15])]);
tmpSh.graphics.drawPath(commands, vvv[trigg]);

и в итоге:
trigg:0; color:0xff0000,0xff00ff
trigg:1; color:0xff0000
trigg:0; color:0xff0000,0xff00ff

Цвета второго прямоугольника игнорированы при любом начальном значении trigg...
Однако, если исказим углы прямоугольников
Код AS3:

var vvv:Vector.<Vector.<Number>> = Vector.<Vector.<Number>>([Vector.<Number>([7, 5, 20, 5, 20, 25, 5, 25, 7, 5]), Vector.<Number>([10, 15, 30, 15, 30, 35, 15, 35, 10, 15])]);

то, надо полагать, благодаря размытию, получим:
trigg:0; color:0xff0000,0xff001f,0xff003f,0xff007f,0xff009f,0xff00bf,0xff00ff
trigg:1; color:0xff0000,0xff00df,0xff009f,0xff005f,0xff001f,0xff00ff
trigg:0; color:0xff0000,0xff001f,0xff003f,0xff007f,0xff009f,0xff00bf,0xff00ff

Попытки отследить связи через отладчик меня не навели ни на какие выводы о причинах такого загадочного поведения... Возможно был недостаточно внимателен, возможно мое знание flash'a and AS изобилует пробелами, что вполне вероятно, т.к. увлекся этим недавно,... но озадачен. Возможно, допускаю некое хулиганство в конструкторе класса, но мне оно не очевидно.

Добавлено через 20 часов 4 минуты
Если код изменить таким образом
Код AS3:

public function MyClass() {
                        var thisbitmapdata:BitmapData = new BitmapData(800, 600, true, 0x80FFFFFF);
                        this.bitmapData = thisbitmapdata;
                        var commands:Vector.<int> = Vector.<int>([1, 2, 2, 2, 2]);
                        var data:Vector.<Number> = Vector.<Number>([5, 5, 19, 5, 19, 19, 5, 19, 5, 5]);
                        var i:int = 0;
                        var pixelValue:uint = 0;
 
                        var paintBm:Function = function(bmp:Bitmap):void {
                                var arr:Vector.<String> = new Vector.<String>();
                                var tmpSh:Shape = new Shape();
                                tmpSh.graphics.beginFill(0xFF00FF, 1);
                                tmpSh.graphics.drawPath(commands, data);
                                var r:Rectangle = tmpSh.getBounds(stage);
                                var w:int = r.width;
                                var h:int = r.height;
                                var bmpdata:BitmapData = new BitmapData(w, h, false, 0xFF0000);
                                bmpdata.draw(tmpSh);
                                var mtr:Matrix = new Matrix(1, 0, 0, 1, 30 + i*10, r.topLeft.y);
                                var aSh:Bitmap = new Bitmap();
                                var aShbmpdata:BitmapData = new BitmapData(w, h, false, 0x00DDCC);
 
                                for (var m:int = 0; m < w; m++) {
                                        for (var j:int = 0; j < h; j++) {
                                                pixelValue =  bmpdata.getPixel(m, j);
                                                if (pixelValue == 0xFF00FF) aShbmpdata.setPixel(m, j, pixelValue);
                                        if (arr.length == 0 || arr.indexOf(("0x"+pixelValue.toString(16))) < 0)  arr.push("0x"+ pixelValue.toString(16));
                                        }
                                }
 
                                arr.splice(0, arr.length);
                                aSh.bitmapData = aShbmpdata;
                                bmp.bitmapData.draw(tmpSh);
                                bmp.bitmapData.draw(aSh, mtr, null, null, null, false);
                                tmpSh.graphics.clear();
                                bmpdata.dispose();
                        }
                        while(i < 20) {
                        for (var e:int = 0; e < data.length; e++ ) {
                                data[e] += i;
                        }
                        paintBm(this);
                        i +=2;
                        }
                }

то создается впечатление, что метод getPixel возвращает пиксели из области, которую объект успешно покидает, и которая определяется начальным getBounds объекта. Не придумал лучшего, как зарегистрировать баг в базе данных Адоба #2933259, но, судя по датам уже имеющихся багов, это надолго.. Я не вижу, в чем может быть некорректность используемого мной приема, а отказываться от него жаль, т.к. сильно экономит код.

Gantenbain 03.08.2011 10:22

Пока считаю это багом
 
Вложений: 1
Получить правильные значения адресов пикселей можно таким образом:
Код AS3:

bmpdata.draw(tmpSh, new Matrix(1, 0, 0, 1, -data[0], -data[1]), null, null, null, false);

но, имхо, это противоречит концепции класса. Пока считаю это багом. Стоит ли использовать, сомневаюсь. Имеются другие мнения?

Wolsh 03.08.2011 12:56

1. Вынесите вложенную функцию. Это – зло в 99% случаев, и лучше так не делать никогда, пока не припрет необходимость.
2. Проблема мне видится в использовании Ректангла, тем более взятого в системе координат стейджа. Я конечно могу ошибаться, ибо код для моего мозга достаточно мутный))))) Вобщем я склонен считать что здесь проблема в тесте, а не баг Адоба. Избавьтесь от ректангла, он тут совершенно не нужен, как я показывал в своем первом посте. Заметьте – это было все, что я изменил в вашем коде, и он заработал.

Gantenbain 03.08.2011 19:11

1. Предлагаете оформить ее методом класса? Методы класса, имхо, должны отражать существенные особенности класса, а повторяющиеся вычисления удобно помещать в такие функции (это, конечно, частные особенности стиля - кто-то их и никогда не использует). Мнение о том, что вложенные функции "зло", встречал, но язык предусматривает такой прием, хотя для пользы дела, если вот такой "тупик", то можно и просто внутри кода разместить повторяющиеся операции ("раздует" его, правда :)).
2. В системе координат объекта (самого, содержащего каверзную БитмапДату класса) точно такая же картина - пробовал. Дело в том, что координаты Ректангла дают действительное размещение объекта в стейдже, именно там он и выводится, и там его место, но "мутность" в том, что последним описанным мной способом, я пишу в БитмапДату Фигуру из области, в которой ее нет :( - вот это меня и удивляет.

P.S. Кстати, как "избавиться от Ректангла" - не понимаю. drawPath у меня рисует фигуру, состоящую из многих Безье-кривых, т.е. чтобы получить адреса пикселей, заполняющих фигуру, я этот Ректангл сканирую.

Wolsh 03.08.2011 20:14

Существенные особенности класса отражают его публичные методы. Приватные методы служат нуждам самого класса или его представителя-экземпляра, они инкапсулированы и являются секретом фирмы, так что никаких особенностей отражать не могут – наоборот, являются внутренними методами для операций, нужных данному классу, и только ему. Это чем-то противоречит цели данной функции?
Цитата:

я этот Ректангл сканирую.
Полученный в координатах стейджа, а наложенный на битмапдату. Зачем Вам этот ректангл? Есть же все что нужно - высота и ширина битмапдаты, и адрес ее первого пикселя - (0, 0)?

Добавлено через 3 минуты
Протрейсите ректангл, может я и зря на него наезжаю, но лучше уж знать, с чем имеем дело.
В моем примере с getVector нет вообще никакого ректангла кроме bmpdata.rect - и то лишь потому что его требует метод.

Gantenbain 04.08.2011 10:50

Цитата:

Это чем-то противоречит цели данной функции?
Нет, но в отсутствие аргументов против использования "внутренних" функций, лишь при наличии мнения, что их использование не есть хорошо, я буду руководствоваться соображениями удобства. Меня убеждают не мнения, а аргументы. В данном случае, не вижу вреда от помещения кода во внутреннюю функцию, удобство, напротив, есть.
Цитата:

Зачем Вам этот ректангл?
Так именно в том и загвоздка, что рисуя в BitmapData, я не нахожу пиксели фигуры в ее адресах! В тех адресах, где она должна по идее размещаться. Посмотрите на левый фрагмент рисунка вставленного в мой предыдущий пост: на нем видно, что, на первом же шаге цикла, квадрат отрисован в BitmapData (bmpdata) с началом не в точке (0, 0), а со смещением в точку, являющуюся начальной точкой его отрисовки - (5, 5) (я размеры битмапдаты специально увеличил сверх размеров фигуры для наглядности, чтобы было видно, что пиксели фигуры смещаются по мере того, как меняется начальная точка ее отрисовки, что я и полагаю багом). Координаты Ректангла позволяют мне следить за этим смещением - это единственное его назначение в данном случае.
Разумеется, строить код на противоречащих концепции языка, или на других приемах, в отношении которых, по любым причинам, испытываешь внутреннее сомнение, плохо. Поэтому, для практических целей, вероятно, стоит поэкспериментировать для начала с другими способами создания графического контента фигуры (я не пробовал старые методы moveTo(), lineTo()),- потому что альтернативы BitmapData нет (ByteArray напрямую из фигуры не создашь, и в нем будут только "кривые" пиксели из битмапдаты).

-De- 04.08.2011 11:23

Цитата:

Сообщение от Gantenbain (Сообщение 1017797)
Посмотрите на левый фрагмент рисунка вставленного в мой предыдущий пост: на нем видно, что, на первом же шаге цикла, квадрат отрисован в BitmapData (bmpdata) с началом не в точке (0, 0), а со смещением в точку, являющуюся начальной точкой его отрисовки - (5, 5) (я размеры битмапдаты специально увеличил сверх размеров фигуры для наглядности, чтобы было видно, что пиксели фигуры смещаются по мере того, как меняется начальная точка ее отрисовки, что я и полагаю багом).

Одно предложение. Код не лучше %) Можно это как-то оформить, чтоб был баг в чистом виде? А то в этом разбираться нет желания (у адобовских камрадов, думаю, тоже). Судя по картинке вы смещаете красные квадратики за зелёные. Ну они и смещаются.

Gantenbain 04.08.2011 12:03

Код AS3:

{
        import flash.display.Bitmap;
        import flash.display.BitmapData;
        import flash.display.Shape;
        import flash.display.Sprite;
 
        public class  Example extends Sprite
        {
                public function Example() {
                        var commands:Vector.<int> = Vector.<int>([1, 2, 2, 2, 2]);
                        var data:Vector.<Number> = Vector.<Number>([5, 5, 19, 5, 19, 19, 5, 19, 5, 5]);
                        var sh:Shape = new Shape();
                        sh.graphics.beginFill(0xFF00FF, 1);
                        sh.graphics.drawPath(commands, data);
                        var bmpdata:BitmapData = new BitmapData(14, 14, false, 0x00FF00);
                        var bmSh:Bitmap = new Bitmap();
                        bmSh.bitmapData = bmpdata;
                        addChild(bmSh);
                }
        }
 
}

Однако, Wolsh, должен еще раз Вас поблагодарить за то, что не давали мыслям успокоиться - чего ради я решил, что битмапдата знает, где начинается моя фигура? :( Я и должен topLeft Rectangle поместить в точку (0, 0) битмапдаты, т.е. это как раз нормальное действие.

to -De-:
Думаю, нет смысла дальше разбираться, drawPath() рисовал мне фигуру в координатах stage, я должен был перерисовать ее, как на холсте в BitmapData, а для этого недостаточно лишь размеров фигуры :), я решил, что BitmapData.draw() сам перенесет "кисть" в начальную точку. Мой косяк. Пойду виниться в Адобе.


Часовой пояс GMT +4, время: 12:02.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.