Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.54/26: Рейтинг темы: голосов - 26, средняя оценка - 4.54
0 / 0 / 0
Регистрация: 11.12.2014
Сообщений: 6

Замена цвета пикселей на черно-белый в выбранном окне

11.12.2014, 22:16. Показов 5182. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Нужно расположить все открытые окна на экране (сделал). Затем выбрать одно из окон, определить его границы и в пределах этих границ сменить цвет на черно-белый. Как сменить цвет пикселей в загруженной картинке я разобрался, а вот со сменой цвета пикселей выбранного окна возникла проблема.
Если я правильно понял, то таким образом можно получить дискриптор окна: HDC hdc = GetDC(Form1->Handle), однако как внедрить это в имеющуюся программу я все равно не понимаю.
Код имеющейся программы:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//---------------------------------------------------------------------------
 
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
HWND hDesktop =GetDesktopWindow();
RECT lpRect;
lpRect.left=0;
lpRect.top=0;
lpRect.right = 1366;
lpRect.bottom = 768;
TileWindows(hDesktop,MDITILE_HORIZONTAL,&lpRect,0,0);        
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  int R,G,B; COLORREF crColor; int F;
 for(int y=0;y<250;y++)
 for(int x=0;x<250;x++)
 {
    crColor = (COLORREF) Image1->Canvas->Pixels[x][y];
       R=GetRValue(crColor);
       G=GetGValue(crColor);
       B=GetBValue(crColor);
       F = (R+G+B)/3;
    Image1->Canvas->Pixels[x][y] = (TColor) RGB(F,F,F);
 }
 
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
if (OpenPictureDialog1->Execute())
Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName); 
}
//---------------------------------------------------------------------------
Файл программы:
Окна.zip
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
11.12.2014, 22:16
Ответы с готовыми решениями:

Черно-белый ковер.
там во вложениях написано

Черно белый график
сам код прост: figure(1); bar(x2,y2'); set(gca,'XTick',) set(gca,'XTickLabel',x2txt,'FontSize', 16); график я перебрасываю в lyx....

Из цветного pdf в черно-белый
Имеется цветной журнал в формате pdf. Хочу загрузить его в эл-ю книгу но размер достаточно большой 105мб, слышал, что средствами фотошопа...

8
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,008
12.12.2014, 04:59
Лучший ответ Сообщение было отмечено SatanaXIII как решение

Решение

После выполнения TileWindows регистрируешь (например) клавишу Escape (для "отлова" нажатий клавиш мыши вне формы нужно, насколько я понял, писать хук):
C++
1
RegisterHotKey(Handle,0,0,VK_ESCAPE);
После того, как 1) кликнешь по окну, 2) нажмешь клавишу и 3) "разукрасишь" окно в черно-белый цвет, сделаешь отмену регистрации (либо при закрытии программы):
C++
1
UnregisterHotKey(Handle,0);
Можно использовать совместно с GlobalAddAtom и GlobalDeleteAtom.

Для определения нажатия клавиши добавь на форму программы компонент ApplicationEvents.
В его событии OnMessage (ApplicationEventsMessage) пропиши:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg, bool &Handled)
{
  if (Msg.message == WM_HOTKEY && Msg.wParam == 0)  // если нажата наша клавиша
  {
    TRect WinRect;
    GetWindowRect(GetForegroundWindow(), &WinRect);  // определяем окно на переднем плане (для этого сначала кликали мышью)
    // делаем допущение, что нет окон, "мешающих" выбранному
 
// дальше часть кода не работает, потому закомментирована
// если кликнуть не по окну, то будет закрашиваться весь экран
//  TPoint point;
//  SetCursorPos(&point);  // определяем позицию курсора
 
    // если мы кликнули по окну (а не по "пустоте")
//  if (point.x >= WinRect.left && point.x <= WinRect.right && point.y >= WinRect.top && point.y <= WinRect.bottom)
    {
      HDC__ *Display = CreateDC(L"DISPLAY", NULL, NULL, NULL);  // создаем "контекст устройства" (КУ) под именем DISPLAY
      unsigned long color;
      int R, G, B, F;
      Graphics::TBitmap *Bitmap = new Graphics::TBitmap;
 
      Bitmap->Width = WinRect.Width();
      Bitmap->Height = WinRect.Height();
      // присваиваем битмапу изображение нашего окна
      BitBlt(Bitmap->Canvas->Handle,0,0,Bitmap->Width,Bitmap->Height,GetDC(0),WinRect.left,WinRect.top,SRCCOPY);
 
      for (int i = 0; i <= Bitmap->Width; i++)
        for (int j = 0; j <= Bitmap->Height; j++)
        {
          color = Bitmap->Canvas->Pixels[i][j];  // определяем цвет точек окна
          R = GetRValue(color);
          G = GetGValue(color);
          B = GetBValue(color);
          F = (R + G + B) / 3;
          Bitmap->Canvas->Pixels[i][j] = (TColor) RGB(F, F, F);  // меняем цвет точек
          SetPixel(Display, i+WinRect.left, j+WinRect.top, Bitmap->Canvas->Pixels[i][j]);  // устанавливаем цвет точек
        }
 
      delete Bitmap;
      DeleteDC(Display);  // уничтожаем КУ
    }
  }
}
Только работает все это медленно (попробуй использовать ScanLine).
Если что-то в окне поменялось, то "завеса" спадает. В этом случае можешь (если будет результат) постоянно перекрашивать окно.
Миниатюры
Замена цвета пикселей на черно-белый в выбранном окне  
2
Почетный модератор
Эксперт С++
 Аватар для SatanaXIII
5851 / 2862 / 392
Регистрация: 01.11.2011
Сообщений: 6,906
12.12.2014, 12:37
Ни на что не претендую, но вот тут кое чего чуть-чуть поломал:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
RegisterHotKey( Handle, 333, 0, VK_ESCAPE );
}
//---------------------------------------------------------------------------
#define TIMEWATCH
void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg,
      bool &Handled)
{
if (Msg.message == WM_HOTKEY)
  switch( Msg.wParam )
  {
  case 333:
    {
    TRect WinRect;
    GetWindowRect(GetForegroundWindow(), &WinRect);  // определяем окно на переднем плане (для этого сначала кликали мышью)
    // делаем допущение, что нет окон, "мешающих" выбранному
 
// дальше часть кода не работает, потому закомментирована
// если кликнуть не по окну, то будет закрашиваться весь экран
//  TPoint point;
//  SetCursorPos(&point);  // определяем позицию курсора
 
    // если мы кликнули по окну (а не по "пустоте")
//  if (point.x >= WinRect.left && point.x <= WinRect.right && point.y >= WinRect.top && point.y <= WinRect.bottom)
//    {
      HDC Display = CreateDC("DISPLAY", NULL, NULL, NULL);  // создаем "контекст устройства" (КУ) под именем DISPLAY
 
      unsigned int color, F; // Если запихать все в size_t, то будет прирост по скорости до нескольких
      char R, G, B;          //  сотен миллисекунд, но памяти потребуется на несколько байт больше. Х)
      Graphics::TBitmap *Bitmap = new Graphics::TBitmap;
 
      Bitmap->Width = WinRect.Width();
      Bitmap->Height = WinRect.Height();
      // присваиваем битмапу изображение нашего окна
      BitBlt( Bitmap->Canvas->Handle, 0, 0, Bitmap->Width, Bitmap->Height,
              GetDC(0), WinRect.left, WinRect.top, SRCCOPY );
#ifdef TIMEWATCH
TTime t, tn = Now().CurrentTime();
#endif
      for (int i = 0; i < Bitmap->Width; i++)
        for (int j = 0; j < Bitmap->Height; j++)
        {
          color = Bitmap->Canvas->Pixels[i][j];  // определяем цвет точек окна
          R = GetRValue(color);
          G = GetGValue(color);
          B = GetBValue(color);
          F = (R + G + B) / 3;
          Bitmap->Canvas->Pixels[i][j] = /*(TColor)*/static_cast<TColor>( RGB(F, F, F) );  // Статическое кастование даст нам еще добрую сотню миллисекунд
          //SetPixel(Display, i+WinRect.left, j+WinRect.top, Bitmap->Canvas->Pixels[i][j]);  // устанавливаем цвет точек
          Application->ProcessMessages(); // Чтобы не висло
        } // Если б мы работали с чужим Bitmap, то для увеличения скорости следовалы бы его канву
          //  заблокировать на время работы цикла: Bitmap->Canvas->Lock()/Unlock()
 
      GetWindowRect(GetForegroundWindow(), &WinRect); // Если вдруг сместилось окно
      BitBlt( Display, WinRect.left, WinRect.top, Bitmap->Width, Bitmap->Height, // Вынесли из цикла
              Bitmap->Canvas->Handle, 0, 0, SRCCOPY);                            //  что уже позволит сократить время обработки более чем на треть
#ifdef TIMEWATCH
t = Now().CurrentTime();
ShowMessage( FormatDateTime( "hh:mm:ss:zzz", t - tn) );
#endif
      delete Bitmap;
      DeleteDC(Display);  // уничтожаем КУ
      }
//    }
  }
}
Средствами GDI быстрее вряд ли получится.

Добавлено через 1 час 2 минуты 3 секунды
Мысль еще - ускориться за счет считывания не попиксельно, а линиями: Bitmap->ScanLine. Но тут опасно. Формат пикселей надо учитывать.
2
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,008
12.12.2014, 14:21
Вчера я затупил, и "выдать" строчки 57-58 вместо SetPixel мозг уже не смог.
Про ScanLine я также упомянул, но хз как с ним работать. Насчет опасности - осторожно, наверно, можно. Опыта нет, пример в билдере бегло просмотрел.
This example shows how to draw directly to a Bitmap. It loads a bitmap from a file and then copies it to another bitmap twice its size. Then the two bitmaps are displayed on the form canvas.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <memory>       //For STL auto_ptr class
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  std::auto_ptr<Graphics::TBitmap> Bitmap(new Graphics::TBitmap);
  std::auto_ptr<Graphics::TBitmap> BigBitmap(new Graphics::TBitmap);
  TRGBTriple *ptr, *bigPtr;   // Use a (byte *) for pf8bit color.
  TPixelFormat pixForm, bigpixForm;
  try
  {
    Bitmap->LoadFromFile("../littlefac.bmp");
    pixForm = Bitmap->PixelFormat;
    bigpixForm  = BigBitmap->PixelFormat;
    Bitmap->PixelFormat = pf24bit;
    BigBitmap->PixelFormat = pf24bit;
    BigBitmap->Height = Bitmap->Height * 2;
    BigBitmap->Width = Bitmap->Width * 2;
    for (int y = 0; y < Bitmap->Height; y++)
    {
      ptr = reinterpret_cast<TRGBTriple *>(Bitmap->ScanLine[y]);
      for (int x = 0; x < Bitmap->Width; x++)
      {
        int bx = x * 2;
        int by = y * 2;
        bigPtr = reinterpret_cast<TRGBTriple *>(BigBitmap->ScanLine[by]);
        bigPtr[bx] = ptr[x];
        bigPtr[bx + 1] = ptr[x];
        bigPtr = reinterpret_cast<TRGBTriple *>(BigBitmap->ScanLine[by + 1]);
        bigPtr[bx] = ptr[x];
        bigPtr[bx + 1] = ptr[x];
      }
    }
    Canvas->Draw(0, 0, Bitmap.get());
    Canvas->Draw(200, 200, BigBitmap.get());
  }
  catch (...)
  {
    ShowMessage("Could not load or alter bitmap");
  }
}
Вроде сродни попиксельной обработке, но с добавлением ускоренного режима работы за счет низкоуровневого доступа (к пикселям).

Добавлено через 21 минуту
P.S.: и дело даже не в том, что SetPixel в цикле. Сам SetPixel "тормозной" до невозможности.
А BitBlt работает реально быстрее.
1
Почетный модератор
Эксперт С++
 Аватар для SatanaXIII
5851 / 2862 / 392
Регистрация: 01.11.2011
Сообщений: 6,906
12.12.2014, 14:42
Вот тут ребята кстати как-то тем же самым баловались: Как попиксельно сравнить 2 изображения

Цитата Сообщение от gunslinger Посмотреть сообщение
за счет низкоуровневого доступа (к пикселям)
В том и шутка - только лишь доступа на чтение. Хотя это уже не плохо. Но надо глянуть описание TRGBTriple. Чуется мне это просто дефайн для uint_8. Типа того.
А вот с записью сложнее. Можно конечно тем же макаром попробовать, только в обратную сторону: получить указатель на линию и писать порциями по TRGBTriple байт. Но как-то все это вилами по воде. Зато скорость будет закачаешься.

VCL просто работает с GDI, и предоставляет хоть и надежные методы, и кучу их всяческих, но все же чудеса с ней трудно вытворять. Вот тут Авазарт как-то баловался с GDI+, которая уже вполне вероятно такое потянет.
Мораль: надо короче либо на уровень ниже переходить, либо выше.

Добавлено через 2 минуты
Цитата Сообщение от gunslinger Посмотреть сообщение
Сам SetPixel "тормозной" до невозможности.
Именно.
Цитата Сообщение от gunslinger Посмотреть сообщение
А BitBlt работает реально быстрее.
Которая уже не из VCL.

Добавлено через 25 секунд
Все сошлось.

Добавлено через 10 минут
Информация в тему. Именно быстрый доступ к пикселям в GDI и в MFC
При написании фильтров для изображения требуется способ доступа к отдельным пикселям. Самый простой способ - сделать это с помощью функций GetPixel/SetPixel в WinAPI и MFC и с помощью двумерного массива TCanvas->Pixels в VCL. Однако так поступать не стоит, поскольку такой способ является чрезвычайно медленным.

Быстрый способ доступа к пикселям в GDI и MFC:

При работе с функциями GDI напрямую, наиболее удобным представляется создание объекта bitmap, к пикселям которого можно обращаться напрямую. Делается это с помощью функции CreateDIBSection. Одним из выходных параметров этой функции является указатель на переменную, куда при создании bitmap будет помещен указатель на массив пикселей - ppvBits. Запомнив этот указатель, приложение получает прямой доступ к пикселям изображения. Обычно использующиеся true color изображения с глубиной цвета 24 bit хранят данные попиксельно в виде массива троек `BGR' (каждый пиксель - три байта).

Адрес пикселя с координатами (x, y) для изображений такого типа рассчитывается следующим образом:
C++
1
ppvBits + y * iBytesPerLine + x * 3

Здесь iBytesPerLine - это длина строки изображения в байтах, которая отнюдь не всегда равна ширине изображения, умноженной на три. Для увеличения производительности работы с изображением адреса начал строк выравниваются по границе процессорного слова (4 байта), поэтому если ширина, умноженная на 3, не кратна четырем, каждая из строк дополняется несколькими дополнительными байтами . Рассчитать длину строки в байтах можно по следующей формуле:
C++
1
iBytesPerLine = (iWidth * 3 + 3) & -4;

Именно таким образом быстрый доступ к пикселям изображения реализован в классе DSimpleBitmap в примере MFC_GML3.

Быстрый способ доступа к пикселям в VCL:

Для того чтобы получить прямой доступ к указателю на пикселы изображения, хранящегося в TBitmap, нужно использовать свойство ScanLine. Это массив указателей на строки пикселей изображения.

Доступ к пикселю с координатами (x, y) осуществляется следующим образом:

C++
1
2
    // pBitmap - указатель на TBitmap обрабатываемого изображения
    pBitmap->ScanLine[y][x * 3]
Формат хранящихся в изображении данных задается свойством PixelFormat объекта TBitmap. Для полноцветных изображений (PixelFormat = pf24bit) каждому пикселю соответствует три байта, задающие интенсивности каждого из цветовых каналов - 'BGR'.

Буду очень сильно благодарен, если кто удосужится провести серию опытов с обоими предложенными методами, и соответственно поделится кодом (чтобы каждый мог у себя проверить) и сравнительными результатами.
1
120 / 142 / 46
Регистрация: 31.10.2014
Сообщений: 721
Записей в блоге: 1
12.12.2014, 15:27
Цитата Сообщение от Ррабочий Посмотреть сообщение
сменить цвет на черно-белый.
Зачем такое извращение ?
0
0 / 0 / 0
Регистрация: 11.12.2014
Сообщений: 6
12.12.2014, 16:52  [ТС]
Зачем такое извращение ?
Такое задание в универе на лабораторную работу.
0
120 / 142 / 46
Регистрация: 31.10.2014
Сообщений: 721
Записей в блоге: 1
13.12.2014, 06:00
Может проще сделать снимок окна, перевести его в Ч/Б и наложить его сверху. Либо создать Layered окно, и наложить его (может быть сделать дочерним) сверху.
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,008
13.12.2014, 11:49
Цитата Сообщение от demmax2004 Посмотреть сообщение
Может проще сделать снимок окна, перевести его в Ч/Б и наложить его сверху.
А код из постов за номерами 2 и 3 что делает? Вопрос риторический.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
13.12.2014, 11:49
Помогаю со студенческими работами здесь

Черно-белый сайт по кнопке!
Подскажите пожалуйста, как сделать, чтобы пользователь по кнопке мог изменить цвета сайта на черно белый, и картинки тоже должны стать...

Цветной BMP в черно-белый
Всем привет! Я выделяю из изображения один цветовой канал и нужно сохранить его в черно-белом виде в BMP файле. Первой мыслью было...

Японский кроссворд(черно-белый)
Пожалуйста подскажите как сделать подсчет черных клеток в кроссворде. Ширина и высота поля не постоянная

Чёрно-белый скрин экрана
всем привет. помогите делать ч.б скрины procedure TForm1.Button4Click(Sender: TObject); {ñêðèí ýêðàíà} var desk:hDC; ...

Преобразовать рисунок в черно-белый по алгоритму
Пиксели рисунка закодированы числами от 0 до 255 (обозначающими яркость пикселей) в виде матрицы, содержащей N строк и M столбцов. Нужно...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка. Рецензия / Мнение/ Перевод https:/ / **********/ gallery/ thinkpad-x220-tablet-porn-gzoEAjs . . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru