Помощь - Поиск - Пользователи - Календарь
Полная версия: Пропадает результат picturebox.
Форум «Всё о Паскале» > Delphi, Assembler и другие языки. > Другие языки
Krjuger
Собственно,есть проэкт в VS 2008 C++,у фрейма есть кнопка и picturebox.По нажатии на кнопку должна отрисоваться сетка из прямоугольников.(100х100 где то) Дело в том что после нажания кнопки происходит отрисовка,но по завершению результат пропадает,приходится нажимать 2 раз,и тогда от нормально и длительно отображается,хотя иногда тоже пропадает.Мне непонятно почему такое может происходить??Еще мне немного не нравится та скорость,с которой происходит отрисовка,можно ли ее как нибудь ускорить?Дело в том,что потом для каждой "клетки" будет задаваться свой собственный цвет.

private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
const int N=100;
double size=1.0;
double x,y;
double h=size/N;
double Xkoef= (pictureBox1->Width)/size;
double Ykoef= (pictureBox1->Height)/size;
Graphics^ g = pictureBox1->CreateGraphics();
Pen^ bluePen = gcnew Pen( Color::Blue,1.0f );
SolidBrush^ hBrush = gcnew SolidBrush (Color::Red);
for ( int i=1 ; i<=N ; i++)
{
for ( int j=0 ; j<=N ; j++)
{
x= (i)*h * Xkoef;
y= (j)*h * Ykoef;
g->FillRectangle( hBrush,Rectangle(x, y,(x + h*Xkoef),(y + h*Ykoef)));
g->DrawRectangle(bluePen,Rectangle(x, y,(x + h*Xkoef),(y + h*Ykoef)));
}
}
}

-TarasBer-
А сколько пикселей занимает 1 прямоугольник? Если примерно до 20х20, то понятно, почему тормоза.
Используй StretchBlt.

Ну или напрямую пиши в видеобуфер.
IUnknown
Krjuger, тип RectangleF как-то больше подходит для заталкивания в него вещественных значений, чем Rectangle. Опять же, не надо перерасчитывать значения X в цикле по J, результаты будут одинаковыми, вынеси вычисления из внутреннего цикла. Но это так, для проформы. На скорость отрисовки влияния не окажет.

С точки зрения алгоритма - все прекрасно, почему сбоит реализация в VS 2008 - непонятно. Проверил на C# - рисует прямоугольнички как положено, ничего не исчезает.

Цитата
Еще мне немного не нравится та скорость, с которой происходит отрисовка,можно ли ее как нибудь ускорить?
Если б ты не написал ниже, что
Цитата
потом для каждой "клетки" будет задаваться свой собственный цвет.
- то ларчик открывался просто: в цикле заполняешь массив RectangleF[], без отрисовки, а потом одним махом g->FillRectangles + g->DrawRectangles. Выигрыш по скорости очень заметен. Но раз для каждой клетки - свой цвет, то можно сэкономить на одной из отрисовок. В цикле делаешь g->FillRectangle, и запоминаешь область. А потом, после циклов - опять же один вызов g->DrawRectangles, и тебе все клеточки обведутся...
-TarasBer-
Кстати, обход по икс должен быть внутри обхода по игреку. Это тоже может ускорить.
Krjuger
Ну просто тут такое дело,что эта задача напрямую связана с той,что была до этого,тобиш расчитать поле температур,но преподавателю оказалось мало полученных результатов,ему еще все в картинках подавай.Суть в том что у меня есть некая фигура заданная в виде массива и в каждой ячейке храниться "температура" в данной точке.Мне надо все отградуировать считая, что минимальная температура, это красный цвет, а максимальная это синий.Граници прямоугольников я потом вообще уберу,потому что преподаватель нам давал код на делфи ,который совершал необходимые действия с словами " портируйте сами".И там было x := ((i-1)*h + h/2) * Xkoef; вместо x= (i)*h * Xkoef;,и выводило явно не то что надо(было небольшое наложение прямоугольников).Поэтому,чтобы исправить, я взял и отрисовывал сетку,чтобы убедиться и исправить,хотя принципи такое наложение возможно поможет для больших прямоугольников,дескать получение промежуточного цвета,хотя я не проверял как себя будет вести это наложение.
IUnknown
Цитата
Граници прямоугольников я потом вообще уберу
Тогда тебе волноваться не о чем. Распахнутое на весь экран окно (при расширении 1920*1080) заполняется разными цветами меньше чем за 2 секунды. Все тормоза - именно из-за отрисовки границ, DrawRectangle работает значительно медленнее, чем FillRectangle...
-TarasBer-
> Распахнутое на весь экран окно (при расширении 1920*1080) заполняется разными цветами меньше чем за 2 секунды.

У тебя тоже селерон-600?
Или может таки применить нормальный StretchBlt?
IUnknown
Цитата
У тебя тоже селерон-600?
Нет, у меня по-прежнему E4400.
Цитата
Или может таки применить нормальный StretchBlt?
Чтоб применить нормальный StretchBlt, он для начала должен быть в .Net, да? Это первое. Второе - я не упирался в оптимизацию: на месте создавал кисти, на месте же пересчитывал координаты, просто, чтобы проверить, насколько тормозит отрисовка границ против заливки. Тормозит. Порядка 12 секунд против примерно двух.

А StretchBlt тебе что, очень поможет (даже если его импортировать), если не все цвета одинаковые? И даже не большинство. Ну, будет работать моментально на красных ячейках. Как начнешь рисовать реальную картинку с градациями цветов - получишь еще бОльшие тормоза. Где выигрыш?
TarasBer
> А StretchBlt тебе что, очень поможет (даже если его импортировать), если не все цвета одинаковые?

Да. Я так понял, ОП хочет вывести маленькую картинку в виде большой. Делает это с помощью кучи прямоугольников. StretchBlt делает то же самое.

> Ну, будет работать моментально на красных ячейках. Как начнешь рисовать реальную картинку с градациями цветов - получишь еще бОльшие тормоза.

StrechBlt тормознее, чем куча прямоугольников?!
То у тебя красно-чёрное дерево быстрее, чем сортировка подсчётом, теперь это.

Изображение
IUnknown
Цитата
То у тебя красно-чёрное дерево быстрее, чем сортировка подсчётом, теперь это.
Это ты о чем, вообще? blink.gif

Цитата
StrechBlt тормознее, чем куча прямоугольников?!
Блин... Да ты пойми, что ТС пишет-то не на WinAPI!!! Он пишет управляемый код. Пока это все преобразуешь в тот самый управляемый из неуправляемого, коим является вызов API-функции - потеряешь время (нет, не ты... Программа потеряет время). Еще раз: если б знать, что ТС будет использовать только 16 цветов - нет проблем, пусть мучается, пишет импорты ДЛЛ, ради того, чтоб программа выполнялась на секунду быстрее. И она БУДЕТ выполняться быстрее. Но если мне надо заполнить область градиентом (а по ходу это - именно то, что ему надо:
Цитата
Мне надо все отградуировать считая, что минимальная температура, это красный цвет, а максимальная это синий
) - то StretchBlt идет таким лесом - что ему даже не снилось. Откуда копировать-то прямоугольнички? А? Это тебе не нарисовать первый, а потом его отштамповать тысячу раз, это КАЖДЫЙ РАЗ НОВЫЙ ЦВЕТ!!!
TarasBer
> Это ты о чем, вообще?

А, это не ты был? Типа я поверил, гы.

> Еще раз: если б знать, что ТС будет использовать только 16 цветов

Какая разница, сколько.

> ради того, чтоб программа выполнялась на секунду быстрее

Вывод картинки в течение двух секунд - это дохрена.

> Откуда копировать-то прямоугольнички? А?

Как откуда. Из другого DC, само собой.
А уж попиксельно вывести массив в DC, созданный через CreateDIBSection - это вообще простой цикл, выполняется очень быстро. Только делать это надо не через SetPixel, а через то самое "нельзя, но если очень хочется, то можно", которое ты так стремишься сделать "нельзя". Понимаешь, все запретные концепции хоть и мешают жить большую часть времени, но когда они становятся смертельно нужны, то приходится опускаться и до них. А терпеть 2-секундный вывод чисто ради принципов - это уже фанатизм.

Или ты подумал, что я для каждого прямоугольника отдельно StretchBlt вызываю? Я что, похож на идиота? Нет, я сначала рисую картинку из "прямоугольничков размером 1х1" (при помощи неприятных и грязных, но простых действий), а потом её растягиваю.
IUnknown
Цитата
А, это не ты был? Типа я поверил, гы.
mad.gif Знаешь, что? Свои фантазии оставь при себе. Я не собираюсь тут еще и за чужие слова отчитываться. Я здесь зарегистрировался под ником IUnknown, в статус через 10 секунд после регистрации записал, под каким ником я еще известен в сети. Ни от кого скрываться я и не думал (происхождение ника объяснить, или сам догадаешься? Если имел дело с интерфейсами - можешь и догадаться) Это понятно? Я надеюсь, к этому вопросу мы больше не будем возвращаться...

Цитата
Только делать это надо не через SetPixel, а через то самое "нельзя, но если очень хочется, то можно", которое ты так стремишься сделать "нельзя".
Работа у меня такая, что "нельзя" - это значит "нельзя". А если очень хочется, и делаешь "можно" - то потом это падает. Причем падает так, и тогда, что исправлять это приходится очень большой ценой. И все равно в итоге "нельзя" - оказывается "нельзя".

Цитата
Понимаешь, все запретные концепции хоть и мешают жить большую часть времени, но когда они становятся смертельно нужны, то приходится опускаться и до них.
Без комментариев. Почему - см. ниже.

Цитата
попиксельно вывести массив в DC, созданный через CreateDIBSection - это вообще простой цикл, выполняется очень быстро.
Быстро - когда неуправляемый код вызывается из неуправляемого же. А вызов unmanaged кода из managed - потеря производительности. В разы... Не забыл? Затраты на вызов могут перекрыть прирост производительности, ага. Было смертельно нужно? Ну, и получил тормоза при смешивании кодов... Чья концепция оказалась в выигрыше?
TarasBer
> А если очень хочется, и делаешь "можно" - то потом это падает.

Что же у вас тестирование не работает?
А ничего, что в основе любого кода, если глубже ковырнуть, лежить то самое "нельзя". И первый компилятор писался на "нельзя", потому что другого не было.
Так то паранойя не имеет смысла, можно много до чего дойти.

> Быстро - когда неуправляемый код вызывается из неуправляемого же. А вызов unmanaged кода из managed - потеря производительности. В разы... Не забыл? Затраты на вызов могут перекрыть прирост производительности, ага. Было смертельно нужно? Ну, и получил тормоза при смешивании кодов... Чья концепция оказалась в выигрыше?

Неужели в точканете всё так плохо? Наверняка там есть готовый класс для копирования массива в память БМП, который там хоть через unsafe{} работает, главное, что он есть и отлажен. Просто надо побольше использовать стандартных алгоритмов для всяких вещей типа растягивания картинки итд, иначе либо руками вычислять все адреса всех пикселей (для игр иногда балуюсь этим), либо тормозить с прямоугольниками.

В конце концов, специально для тех, кому страшно, можно для сравнения нарисовать маленькую картинку (с прямоугольниками 1х1) через тупой SetPixel, и растянуть её
Цитата((с))

готовым отлаженным средством, а не изобретать велосипед.
Krjuger
Я благодарен за такой интерес к этой теме,но после 4 или 5 поста я перестал понимать вообще что либо.
Цитата
Еще раз: если б знать, что ТС будет использовать только 16 цветов - нет проблем

Цвета будет всего 2 красный и синий,а все промежуточные цвета будут вычисляться.
Если заглянуть в то,что давал преподаватель на делфи,то там это выглядело так.
Цитата

// коэффициент интенсивности текущего выводимого значения
// Ckoef = 0 соответствует минимальному значению и красному цвету
// Ckoef = 1 соответствует максимальному значению и синему цвету
Ckoef := (U(i,j) - minU) / (maxU - minU);
// Определение цвета
// Первое слагаемое дает вклад оттенка красного (1 байт цвета)
// второе слагаемое - оттенка синего (3 байт цвета)
// 2 байт цвета (оттенки зеленого) при этом всегда остается нулевым
col := Round($FF * (1-Ckoef)) + Round($FF * Ckoef)*$10000;

Собственно вопрос.Хоть я и не знаю делфи,но с данными коментариями я вполне понимаю, как это интерпретируется,но как это реализовать на с++ не совсем представляю,т.к. с графикой встречался весьма редко и не настолько "глубоко".

Цитата
Неужели в точканете всё так плохо?

Там может быть все и хорошо,но я к счастью, или к несчастью, не на нем пишу.
IUnknown
Цитата
Цвета будет всего 2 красный и синий,а все промежуточные цвета будут вычисляться.
Значит, они все-таки будут. То есть, цветов будет уже не 2. Ну, скажем, оттенков цвета...

Блин... Тормоз я, не посмотрел сразу на прототип. Krjuger, внимательно посмотри, что требует на входе FillRectangle?

Цитата(MSDN)
public:
void FillRectangle(Brush^ brush,
float x, float y, float width, float height)
Чуешь, да, куда я клоню? Ширину и высоту 3 и 4 параметром. А не конечную точку. То есть, у тебя отрисовывалось не много маленьких прямоугольников, а много больших, причем постоянно увеличивающихся в размере. Итого, вот такой код:
		void Button1Click(object sender, EventArgs e)
{
const int N=100;
float size=1.0f;
float h=size/N;
float Xkoef= (pictureBox1.Width)/size;
float Ykoef= (pictureBox1.Height)/size;
Graphics g = pictureBox1.CreateGraphics();
Pen bluePen = /*gc*/new Pen( Color.Blue, 1.0f );
Pen redPen = /*gc*/new Pen( Color.Red, 1.0f );
SolidBrush hBrush = /*gc*/new SolidBrush (Color.Red);
SolidBrush hBrush2 = /*gc*/new SolidBrush (Color.Blue);

float y = 0;
for(int j = 0; j <= N; j++)
{
float x = h * Xkoef;
for(int i = 1; i <= N; i++)
{
if ((i + j) % 2 == 1)
{
g.FillRectangle( hBrush, x, y, h * Xkoef, h * Ykoef);
}
else
{
g.FillRectangle( hBrush2, x, y, h * Xkoef, h * Ykoef);
}
x += h * Xkoef;
}
y += h * Ykoef;
}
}
работает на порядок быстрее...

Цитата
я к счастью, или к несчастью, не на нем пишу.
А на чем же ты пишешь? Graphics^, SolidBrush^, System::Object^, System::EventArgs^ - это что, по-твоему? WinAPI?
Krjuger
Да я сам чето тормознул,повелся на то,что преподаватель давал......
Цитата
А на чем же ты пишешь? Graphics^, SolidBrush^, System::Object^, System::EventArgs^ - это что, по-твоему? WinAPI?

Да я чуш сморозил,у меня почему то проассоциировалось с C#.net,сам незнаю почему.

Вопрос о том, как создать оттенок зная пропорции синего и красного, в силе.
Я немного покопавшись нарыл такую вещицу ,как Color::FromArgb(R,G,B); при помощи которого в цикле создавать нужный мне цвет и закрашивать его.
Что то в духе.

SolidBrush^ hBrush = gcnew SolidBrush (Color::FromArgb(255,0,0));


А потом в цикле делать
hBrush = gcnew SolidBrush (Color::FromArgb(2*i,0,j));

Для примера.
Но мне не нравится использовать gcnew в цикле,хоть и выделяется памяти немного.А как выкрутиться без него пока что не нашел решения.
IUnknown
Зачем же выделять память, если можно сделать изменение свойства:
SolidBrush^ hBrush = gcnew SolidBrush (Color::FromArgb(255,0,0));

// а потом, в цикле:
hBrush->Color = Color::FromArgb(2*i, 0, j);
?
Krjuger
Спасибо за совет,сам бы врятли додумался smile.gif
Новую тему создавать не стал,так как немного мелочным посчитал проблему.
У меня есть файл в котором лежит число(размерность матрицы),затем исходная матрица и преобразованная матрица(размерность одинаковая).Можно ли как то считать быстро ненужную мне исходную матрицу ,чтобы быстро приступить к нужной,или можно сразу как нибуть переместиться на нужную позицию,но как тогда вычислить где именно необходимая мне позиция.

Так же меня немного смутил такой момент,когда я записывал в файл,то делалось это достаточно просто.

freopen("info.txt","w",stdout);
cout<< m*N << '\n';


и тд,но сделать подобное с cin у меня не получилось,как не странно.
Пришлось сделать

FILE* pFile;
pFile=fopen("info.txt","r");
for(int i = 0; i <= N; i++)
{
for(int j = 0; j <= N; j++)
{
fscanf(pFile,"%lf",&U[i][j]);
}
}


Есть ли возможность как то через cin это провернуть.
Ну или можно создать диалог с открытием файла и пойти через StreamReader.
System::IO::StreamReader ^ myStream = gcnew System::IO::StreamReader(openFileDialog1->FileName);
В общем незнаю что сделать....
IUnknown
Цитата
Есть ли возможность как то через cin это провернуть.
Молодец. Перенаправить вывод в stdout в файл - догадался, а перенаправить чтение из файла в stdin что, нельзя? smile.gif

freopen("info.txt","r", stdin);
// ...
cin >> value;
Но я бы все-таки сделал это через StreamReader:

		void btnReadMatrixClick(object sender, EventArgs e)
{
openFileDialog1.ShowDialog();
TextReader sr = new System.IO.StreamReader(openFileDialog1.FileName);
int Len = Convert.ToInt32(sr.ReadLine());

string s = sr.ReadToEnd();
string[] sArr = s.Split('\n');

int[,] iArr = new int [Len, Len];

// Вот, собственно, пропускаем первую часть, от 0 до Len - 1...
for(int i = Len; i < sArr.Length; i++)
{
int j = 0;
foreach(string st in sArr[i].Split(' '))
{
iArr[i - Len, j++] = Convert.ToInt32(st);
}
}
}
(VS у меня нету, так что код - на C#). Тестировал на небольших матрицах (элементы в строке разделены пробелами, но в принципе, Split принимает любой разделитель...) - все прекрасно работает.
Krjuger
Ну а тут возникли некоторые проблемы.
Дело в том,что Split,как утверждает MSDN работает в С++ немного не так как в C#.
Цитата

[C#] public string[] Split(params char[]);

[C++] public: String* Split(__wchar_t __gc[]) __gc[];

Да и твой код я так в полной мере не понял.
что значит "," в строчке int[,] iArr = new int [Len, Len];
Я пытался все исправить руководствуясь тем,что от меня требовал компилятор и с 15 ошибок сократил до 5,но все 5 выдаются как раз на этой строчке.
Получилось так.

int Len = Convert::ToInt32(myStream->ReadLine());
String^ s = myStream->ReadToEnd();
array<String^>^ sArr = s->Split('\n');

int[,] iArr = new int [Len, Len];

// Вот, собственно, пропускаем первую часть, от 0 до Len - 1...
for(int i = Len; i < sArr->Length; i++)
{
int j = 0;
for each(String^ st in sArr[i]->Split(' '))
{
iArr[i - Len, j++] = Convert::ToInt32(st);
}
}


Пришлось после всех String-оф понаставить "^",потому что компилятор писал.
Цитата
error C3149: 'System::String' : cannot use this type here without a top-level '^'

Как это будет работать я даже не знаю,потому,что оно даже не компилируется пока что.
Ошибки на строке int[,] iArr = new int [Len, Len]; следующие
Цитата

error C2143: syntax error : missing ';' before '['
error C3409: empty attribute block is not allowed
error C2143: syntax error : missing ']' before ','
error C2143: syntax error : missing ';' before ','
error C2065: 'iArr' : undeclared identifier

И наконец,для чего вообще нужен массив iArr,в него закидывается то что мне нужно,как я понял.
IUnknown
Цитата
Да и твой код я так в полной мере не понял.
Что именно не понятно? Вроде все прозрачно: читаем из потока первую строку, там - размерность, конвертируем ее в целое число, запоминаем в Len. Дальше - читаем оставшуюся часть файла (ReadToEnd), и разбиваем ее на строки (разделитель - конец строки, или '\n'). Первые Len строк /от 0 до Len - 1/ нас не интересуют, поэтому их не обрабатываем. Начинаем с индекса Len, и до конца массива. Что делать с каждой из строк? Разбить ее по пробелу на последовательность элементов строки и каждый сконвертировать в целое число. Для этого нужен двумерный массив: int [ , ] значит массив с двумя измерениями.

Цитата
И наконец,для чего вообще нужен массив iArr
blink.gif Ты меня спрашиваешь? Тебе надо было, не мне:
Цитата
У меня есть файл в котором лежит число(размерность матрицы),затем исходная матрица и преобразованная матрица(размерность одинаковая).Можно ли как то считать быстро ненужную мне исходную матрицу ,чтобы быстро приступить к нужной
Вот iArr - и есть нужная матрица. Целочисленная, если что.

Насчет C++/CLI ничем помочь не могу, кроме ссылки. Вот тут посмотри:
How to read in a two dimensional array or any dimensional array from a file? , там решается подобная задача. Аналогичным методом.
Krjuger
Возвращаясь к тем же баранам.Тот код ,что ты скидывал прекрасно работает для целых чисел.Я заменил строку.
iArr[i - Len, j++] = Convert::ToInt32(st); на iArr[i - Len, j++] = Convert::ToDouble(st); и тут возникли проблемы,когда число целое все прекрасно скидывается и записывается,но когда число дробное,то выдает сразу ошибку.
Цитата

An unhandled exception of type 'System.FormatException' occurred in mscorlib.dll

Additional information: Входная строка имела неверный формат.

Я попробовал сделать так.

String^ GetExceptionType( Exception^ ex )
{
String^ exceptionType = ex->GetType()->ToString();
return exceptionType->Substring( exceptionType->LastIndexOf( '.' ) + 1 );
}

void ConvertToDouble( String^ numericStr, Object^ defaultValue )
{
try
{
defaultValue = Convert::ToDouble( numericStr );
}
catch ( Exception^ ex )
{
defaultValue = GetExceptionType( ex );

}


и вызывать

ConvertToDouble( st, temp );
U[i - Len][j++] =temp;


Но теперь уже в темп ничего не записывается.В общем опять нужна помощ.
Когда я руками заменил вместо "." на "," все нормально отработало с U[i - Len][j++] = double::Parse(st);(его я тоже попробовал),да и с U[i - Len][j++] = Convert::ToDouble(st); тоже.
Но я считаю,что руками это делать,это во-первых занятие неблагодарное,а во-вторых,не по программистски)
А еще меня ооооочень интересует что за у.... нехороший человек придумал,что число дабл храниться в в памяти с точкой и соответственно записывается в файл с точкой,а вот считыватся должно с запятой,а потом преобразуется с точкой.
-TarasBer-
> ,что число дабл храниться в в памяти с точкой

ЧЁ

Просто во американским стандартам, отражённым во всех ЯП, вещественные числа пишутся через точку. Но дядя Билл решил, что раз в России принята запятая, то пусть и стандартный разделитель в русской версии винды будет запятая, что влияет на НЕКОТОРЫЕ функции ввода (и преобразования строк в числа).

Короче, я не знаю, как в С++.НЕТ это делается, но у тебя либо есть способ поменять разделитель для функции преобразования, либо взять другую функцию для преобразования строки в число.
Krjuger
Да решение оказалось действительно достаточно простым.

NumberFormatInfo^ Provider = gcnew NumberFormatInfo;
Рrovider->NumberDecimalSeparator = ".";
// и вызов
U[i - Len][j++] = Convert::ToDouble(st, Provider);

Krjuger
В общем я наконецто доделал.Протестировал на разных N правда до 45,вроде работает.
Если надо, могу выложить код.
Для моих граничных условий и функции при N=45 получается такой результат:
Изображение
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.