C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Показать TIFF рисунок, ... как собственно это сделать?
vvoid
Отправлено: 10.02.2006, 18:13


Машинист паровоза

Группа: Участник
Сообщений: 171



Всем всего.

Меня интересует, как отобразить рисунок формата TIFF в своей программе? Если конкретнее то мне надо отображать файлы tiff сжетые без потерь, но я думаю это не существенно...

Спасибо за помощь.
Aptem
Отправлено: 11.02.2006, 06:56


Мастер участка

Группа: Участник
Сообщений: 349



Разберитесь со структурой TIFF-файлов, выдерайте оттуда сам рисунок, формируйте bmp-файл и запихивайте его на TImage, либо ищите компоненты для работы с TIFF-файлами.
vvoid
Отправлено: 13.02.2006, 11:46


Машинист паровоза

Группа: Участник
Сообщений: 171



.... как-то не смешно sad.gif
Не думаю, что даже в винде всё так запущено... Наверняка есть возможность сделать это более стандартным способом. Ведь и на MS VC можно отображать рисунки, а там зависимость от сторонних компонентов прослеживается явно меньше.
olegenty
Отправлено: 13.02.2006, 11:57


Ветеран

Группа: Модератор
Сообщений: 2412



1. Зарегистрировать свой класс TTIFFImage (тогда TIFF резко станет можно отображать прямо посредством компоенента TImage). При этом, есс-но, класс TTIFFImage будет уметь прочитать содержимое файла TIFF да и изобразить его.
2. Найти какой-нибудь "узкозаточенный" компонент специально для отображения данного типа файлов
3. Качнуть из раздела VCL закрытой части данного форума библиотеку Image Lib и не париться.

Aptem
Отправлено: 13.02.2006, 19:21


Мастер участка

Группа: Участник
Сообщений: 349



Посмотрите здесь http://rxlib.ru/prim/tiff.rar
vvoid
Отправлено: 22.02.2006, 19:59


Машинист паровоза

Группа: Участник
Сообщений: 171



Вообщем решил делать так:
1. В WinXP вместе с появлением .net появилась библиотека GDI+, которая поддерживает работу с разными форматами изображений.
2. В GDI+ есть класс Graphics, который умеет выводить на экран изображения разных форматов. Создать экземпляр этого класса можно зная лишь HDC.
3. TImage->Canvas->Handle и есть HDC, который можно использовать для работы с GDI API. Так написано в хелпе.

Функции вывода на HDC изображения написал на .net. Остальное GUI на Builder 5 (мы используем именно эту версию билдера). Связал это всё при помощи dll, в ней фактически то, что реализовывалось на .net.

Всё нормально работает и без сторонних компонентов. smile.gif

А теперь вопросы.... smile.gif так как и без них не обошлось.


Проблема возникает, когда пытаюсь на одном TImage отображать разные рисунки разных размеров. Делаю так: получаю размер рисунка, изменяю размер TImage, после чего вызываю функцию отображения рисунка. Лажа проявляется, когда я пытаюсь отобразить сначала маленький рисунок, потом большой. Большой рисунок получается обрезаный по размерам предыдущего маленького рисунка. Размеры TImage-а точно изменяються (специально проверил). Создается впечатление, что не меняются именно размеры, которые хранятся где то в HDC (точнее в неком DeviceContext, с которым связан этот handle).

Вот теперь вопрос. Кто-нибудь знает, как поменять размеры региона для вывода графики в HDC.

PS
Из этой ситуация я выкрутился методом динамического создания объектов TImage непосредственно перед выводом (естественно удалив перед этим прошлый), но это как-то не эстетично smile.gif.

PPS Извиняюсь за долгую прелюдию, но действовал по принципам:
1. вопрос задан — должен быть ответ (Это про первую часть поста)
2. чтобы получить ответ нужно информативно задать вопрос (Это про вторую часть поста)

Спасибо за помощь.

Отредактировано vvoid — 23/02/2006, 13:40
Grigoriy
Отправлено: 06.03.2006, 17:28


Мастер участка

Группа: Участник
Сообщений: 381



QUOTE
Делаю так: получаю размер рисунка, изменяю размер TImage, после чего вызываю функцию отображения рисунка.


И изменяется при этом TImage->Canvas->Handle.
Вот проверьте. После изменения размеров TImage изменяется контекст устройства.

Нужно снова извлечь контекст устройства из TImage->Canvas->Handle.
vvoid
Отправлено: 06.03.2006, 18:13


Машинист паровоза

Группа: Участник
Сообщений: 171



что значит
QUOTE
извлечь контекст устройства из TImage->Canvas->Handle.

Grigoriy
Отправлено: 06.03.2006, 19:17


Мастер участка

Группа: Участник
Сообщений: 381



что значит извлечь контекст устройства из TImage->Canvas->Handle ?

А TImage->Canvas->Handle это контекст устройства, компоненту, в данном случае экземпляру TImage.

QUOTE
Создается впечатление, что не меняются именно размеры, которые хранятся где то в HDC (точнее в неком DeviceContext, с которым связан этот handle).


Ну вот, что то вы подобное уже написали, только в данном случае handle это и есть сам DeviceContext.

Попробуйте вот такой код.
CODE

//..............
HDC DC1;
tagPOINT* p1=new tagPOINT;
DC1=Image1->Canvas->Handle;
MoveToEx(DC1,10,10,p1);
LineTo(DC1,100,100);
//..............


На Image1 должна нарисоваться линия. Правда сам компонент заполняется белым фоном чего-то (на это есть свои причины, о которых я пока не знаю).

Когда изменяется ширина или высота компонента, то меняется и контекст устройства, связанный с ним.

А вообще, я сейчас пробую изменяя размеры компонента и снова после этого присваивая DC1=Image1->Canvas->Handle;, что-то нарисовать. И
непонимаю, почему не получается так как я задумал.
Изменяю ширину компонента, линия не рисуется полностью, так как будто ширина и не изменилась, хотя я же заново контекст обновил
В общем надоело возиться sad.gif sad.gif .

Компонент сам выполняет какой-то свой код.
Наверно компонент Image1
просто "не хочет" присваивать себе больший размер до тех пор, пока это не потребует загружаемое изображение, например то, которое загружается из файла. Так что не надейтесь на то, что компонент послушно так быстро сразу изменит свои размеры. Если он вас не устраивает, забудьте о нем. Есть другие альтернативные пути вывода изображений через функции API.
vvoid
Отправлено: 06.03.2006, 19:28


Машинист паровоза

Группа: Участник
Сообщений: 171



QUOTE
Изменяю ширину компонента, линия не рисуется полностью, так как будто ширина и не изменилась, хотя я же заново контекст обновил

Вот-вот, похоже и вы наткнулись на ту же проблему. Только сформулировали её лучше. У меня тоже происходит так, как будто я и не менял размеры Image-a.

QUOTE
Есть другие альтернативные пути вывода изображений через функции API.

Я тоже уже начал думать об этом. smile.gif Вы о каких альтернативных путях говорите?
Я начал пробовать выводить изображение на TPanel, получая HDC при помощи GetDC(...). Но тут проблема с перерисовкой. надо наверное самому писать обработчик на WM_PAINT.... Кстати как это в Builder-е сделать?

...... Может я и зациклился на этих HDC...
vvoid
Отправлено: 06.03.2006, 19:32


Машинист паровоза

Группа: Участник
Сообщений: 171



QUOTE
handle это и есть сам DeviceContext

я имел в виду, что сам DeviceContext это некая структура, бог весть где хранящаяся, а HDC это Handle с ипользованием которого к ней организовывается доступ.

.... но суть не в этом ...
Grigoriy
Отправлено: 08.03.2006, 08:46


Мастер участка

Группа: Участник
Сообщений: 381



QUOTE (vvoid @ 06/03/2006, 19:28)
Вы о каких альтернативных путях говорите?

Попробуйте использовать
TPaintBox
Если не получиться, то создайте сами дочерние окна в нужных позициях. Это можно сделать с помощью API.
И в структуре, передаваемой API-функции регистрации класса окна, имеется составляющая со значением адреса оконной функции. А там в оконной функции нужно анализировать сообщение и если оно WM_PAINT, то перерисовываете изображение.

Только в API сначала
функцией
RegisterClassEx(const tagWNDCLASSEXA)
регистрируется класс окна, а потом уже
функцией API
CreateWindowEx
создаем окно
и функцией API
ShowWindow
отображаем его.

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

И так, я сейчас приведу Вам код на C++ Builder в котором я не использовал ничего от класса TForm и создавал полноценные окна.

Несмотря на то, что я не использовал класс TForm для создания окна, размер мною написанного кода оказался не очень большим. Можно сказать, что даже все видно как на ладони !
И я вам объясню, как можно создавать окна разных стилей.

CODE

//!!! Глобальные переменные !!!
WNDCLASSEX* cl1= new WNDCLASSEX;//структура описания класса окна
int a=0;
AnsiString str1;
//Простенькая оконная функция
long __stdcall meswin1(HWND v1, unsigned int p1, unsigned int p2, long p3)
{
if (p1==WM_PAINT)
{
a++;str1="Это окно создано функциями API и перерисовано уже "+IntToStr(a)+" раз \0";
SetWindowText(v1,str1.c_str());
};
DefWindowProc(v1,p1,p2,p3);
};

//В обработчике нажатия кнопки регистрируем класс окна,
//создаем это окно,
//показываем это окно
void __fastcall TForm1::Button4Click(TObject *Sender)
{
HMODULE hht1;
HICON ic1;
HCURSOR cur1;
ATOM atom1;
HWND wd1;
char* namecl="API-Window\0";
char* namew="This window created API\0";
cl1->cbSize=sizeof(WNDCLASSEX);//размер структуры
cl1->style=CS_HREDRAW+CS_VREDRAW;//Class styles
cl1->cbClsExtra=0;
cl1->cbWndExtra=0;
hht1=GetModuleHandle(0);/*базовый адрес модуля запрашивающего класс окна*/
cl1->hInstance=hht1;
ic1=LoadIcon(hht1,0);//иконка окна
cl1->hIcon=ic1;
cur1=LoadCursor(hht1,0);//курсор окна
cl1->hCursor=cur1;
cl1->hbrBackground=GetStockObject(LTGRAY_BRUSH);//фон окна
cl1->lpszMenuName=0;//имя меню (в данном случае не используется)
cl1->lpszClassName=namecl;//имя класса окна
cl1->hIconSm=0;
cl1->lpfnWndProc=meswin1;// адрес оконной функции
atom1=RegisterClassEx(cl1);//зарегистрируем класс окна
//Теперь создадим окно являющееся экземпляром класса окна
//Окон одного и того же класса может быть несколько
wd1=CreateWindowEx(WS_EX_APPWINDOW,namecl,namew,WS_OVERLAPPEDWINDOW,10,20,600,400,Form1->Handle,0,hht1,0);
//Теперь покажем окно
ShowWindow(wd1,SW_SHOWNORMAL);
}


Вот в принципе и все, что нужно для создания окна...
Когда нажимаем на кнопку Button4,
появляется окно. Это окно при сворачивании сворачивается в панель задач, потому что в значении расширенного стиля я поставил WS_EX_APPWINDOW (помните обсуждалось уже на форуме как сделать чтоб сворачивалось в панель задач?).
Значение Form1->Handle — дескриптор окна-родителя.
Когда вы создаете форму на этапе проектирования, то это окно будет при прогоне программы дочерним окном главной формы, вот поэтому приходится дополнительно указывать расширенный стиль. Но если вместо Form1->Handle прописать 0, то и вместо WS_EX_APPWINDOW можно писать 0, и окно будет тоже сворачиваться в панель задач.
Далее попробуйте поставить в качестве расширенного стиля WS_EX_LAYOUTRTL и создайте окно. Системные кнопки будут на нем находится в обратном порядке.
Координаты левого верхнего угла у вышеописанных создаваемых окон отсчитываются от левого верхнего угла рабочего стола.

Но нам нужно вывести изображение.
И так. В качестве четвертого параметра в API-функции создания окна используем WS_CHILD, и обязательно в девятом параметре сообщим дескриптор родительского окна, иначе ОС просто не будет знать в каком окне изображать наше окно. Первый параметр будет 0.
И теперь координаты левого верхнего угла у такого окна будут отсчитаны относительно левого верхнего угла окна-родителя.

Далее просто функцией GetDC находим контекст устройства и вперед...
vvoid
Отправлено: 09.03.2006, 12:39


Машинист паровоза

Группа: Участник
Сообщений: 171



Спасибо за экскурс в WinAPI smile.gif Узнал нечто полезное... особенно про WindowStyle константы (WS_XXX). Но учитывая то, что на форме необходмо присутствие не только рисунка, а и всяческих контролов, события который естессно прийдётся обрабатывать, то желание делать вся на чистых апях куда-то пропало.

А вот на TPaintBox всё очень даже хорошо отрабатывает. Он чётко меняет (обновляет) HDC и на нём всё очень даже хорошо отрисовывается. Причём всё весьма красиво ложится с использованием двойной буферизации. Суть в том, что в памяти создаётся CompatibleDC, на него рисуется, а обработчик OnPaint представляет собой всего лишь BitBlt этого DC на PaintBox.

А вот дальше снова проблемы... теперь с масштабированием. Опять же напросилось красивое решение с так сказать "тройной" буферизацией. Есть DC на котором оригинальная картинка. Есть DC на котором преобразованная (масштабированная картинка полученная при помощи StretchBlt из оригинальной). Именно масштабированная картинка выводится на PaintBox при помощи всё тогоже BitBlt. Всё вроде работает... но качество это просто кошмар. Даже при незначительном уменьшении картинки она ужасно темнеет.

Кстати, если по другому масштабировать изображение, то всё нормально.
По другому это:
как я уже писал, для вывода я использую GDI+, там при выводе изображения можно указатьв какой четырёхугольник (не обязатьельно с прямыми углами) выводить изображение, и оно при этом не обрезается а преобразовывается. Так вот, это занимает достаточно длительное время, как я понимаю картинка фактически заново загружается, а если там 24-битный скан А4 листа в 600 dpi то это более 100 МБ... и следует заметить, что качество полученного изображения на много лучше...

Может быть проблема где-то в "настройках" StretchBlt, там вроде есть возможность уазывать функции "правильный путь".

Может будут каке идеи?
olegenty
Отправлено: 09.03.2006, 12:51


Ветеран

Группа: Модератор
Сообщений: 2412



2 vvoid — если это ковыряние для себя или стартап какого-то большого проекта, то без сомнения стоит разобраться в проблеме. если же это конкретное задание, ограниченное по срокам, то ImageLib рулит. несколько кликов мыши, и весь описанный функционал работает.
vvoid
Отправлено: 09.03.2006, 12:58


Машинист паровоза

Группа: Участник
Сообщений: 171



Я уже когда-то на форуме спрашивал, кому надо платить з ImageLib?

Извиняюсь, если по своей невнимательности не уловил наличие ответа на этот вопрос и прошу его повторить, если это так.
vvoid
Отправлено: 09.03.2006, 13:15


Машинист паровоза

Группа: Участник
Сообщений: 171



Вопрос про масштабирование отпал. smile.gif
Всё проще гараздо, нужно использовать SetStretchBltMode и установить HALFTONE режим. Он занимает некоторое время, но своё отрабатывает, картинка получается классная.
vvoid
Отправлено: 13.03.2006, 18:08


Машинист паровоза

Группа: Участник
Сообщений: 171



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

Надо отобразить большой рисунок... Ну для примера возьмём tiff, 600 dpi, A4, 24 бита (полноцвет). Это где-то 110 МБ... Кто-нибудь может подсказать как это сделать? Проблема в том, что возникает ошибка о нехватке памяти.

Обычный код, который позволяет получить "совместимый" DC, приводит к ошибке:
CODE

TestDC = CreateCompatibleDC(PaintBox->Canvas->Handle);

 if (TestDC == NULL)
  {
   ErrorCode = GetLastError();
  }
 else
  {
   TestBM = CreateCompatibleBitmap             // Это приводит к ошибке
    (PaintBox->Canvas->Handle,
     TestWidth,
      TestHeight);

   if (TestBM == NULL)
    {
     ErrorCode = GetLastError();        // код ошибки 8 — "мало памяти"
    }
   else if(SelectObject(TestDC,TestBM) == NULL)
    {
     ErrorCode = GetLastError();
    }
  }


Вот такое г... Может у кого есть идеи по преодолению этой проблемы?
Grigoriy
Отправлено: 13.03.2006, 19:14


Мастер участка

Группа: Участник
Сообщений: 381



Я просто не пойму, почему же не хватает памяти.
Ну возьмем к примеру лист формата А4.
297*210 мм
Это площадь 96.6736 дюймов квадратных.
Если разрешение 600 точек на дюйм, то количество точек
600*600*96.6736=34802530
И с учетом того, что на каждую точку приходится по 4 байта
34802530*4=139210120 байт.
Приблизительно 133 МБ.
Оперативной памяти свободной вроде бы больше. Не так ли ?
Или может быть есть некое ограничение в Windows ?
Или размер карты бит вычисляетя не так ?

Я попробую созавать карту бит такого размера на своей машине, где у меня всего 512 МБ оперативной памяти. Я посмотрю в диспетчере задач на сколько увеличилась занимаемая память.

Отредактировано Grigoriy — 13/03/2006, 19:15
vvoid
Отправлено: 13.03.2006, 19:26


Машинист паровоза

Группа: Участник
Сообщений: 171



QUOTE
Или может быть есть некое ограничение в Windows ?

Вот и меня посетил такой вопрос.
попробуйте у себя выполнить приведённый мною код...

Вот полный код функции
CODE

#define TestWidth                (5000)
#define TestHeight               (7000)

//---------------------------------------------------------------------------

void __fastcall    TForm1::Button1Click
                                (TObject                 *Sender)
{
 DWORD                           ErrorCode;
 HDC                             TestDC;
 HBITMAP                         TestBM;

 ErrorCode = NO_ERROR;

 TestDC = CreateCompatibleDC(PaintBox->Canvas->Handle);

 if (TestDC == NULL)
  {
   ErrorCode = GetLastError();
  }
 else
  {
   TestBM = CreateCompatibleBitmap(PaintBox->Canvas->Handle,
                                   TestWidth,
                                   TestHeight);

   if (TestBM == NULL)
    {
     ErrorCode = GetLastError();
    }
   else if(SelectObject(TestDC,TestBM) == NULL)
    {
     ErrorCode = GetLastError();
    }
  }
}



Отредактировано vvoid — 14/03/2006, 13:21
MarcusCaesar
Отправлено: 15.03.2006, 00:19


Не зарегистрирован







Данный вопрос решался мной некоторое время назад. Рекомендую использовать библиотеку GDI+ (полное описание см.на сайте RSDN — Часть2. Работа с растрами и графическими файлами).
Используя класс Graphics с его массой функций вывода типа DrawImage, можно отображать картинки практически любого размера в каком угодно масштабе, с поворотом и заменой цветов, прозрачностью и кучей другой гадости, которую только можно придумать при работе с картографическими системами (векторная+растровая графика).
Конкретно я работал функциями GDI+ на компоненте TPaintBox. Особых нареканий нет ни у меня ни у заказчиков.
Серия статей на RSDN (их всего 3 штуки) отлично все описывает. Но при каких-то сложностях можешь поискать примеры на сайте "Королевство Делфи" (адрес к сожалению не помню).
В случае неясностей можешь написать письмо мне на MarcusCaesar@list.ru. Постарають ответить побыстрее, и по возможности полно.
vvoid
Отправлено: 15.03.2006, 16:16


Машинист паровоза

Группа: Участник
Сообщений: 171



Спасибо MarcusCaesar за помощь и за интересные статьи.

Я ведь тоже использую GDI+, о чём писал выше...

Как раз сейчас мне удалось связать GDI+ и PaintBox в один узел

Всё проще гораздо, чем я думал (часто так бывает, ищешь каких-то сложных решений, считая, что прямое решение не подойдёт, а оно как раз и подходит) smile.gif
Можно обойтись безо всяких CompatibleDC, а писать прямо на PaintBox->Canvas->Handle в обработчике OnPaint (Видимо PaintBox каким-то образом обходит ограничение на размер HBITMAP ... инетерсно каким). Всё рисуется быстро безо всяких задержек. Но для этого надо разумеется держать файл в памяти. Здесь и зарыта собака.
Получается, что объём памяти занимаемой прогой в два раза больше размера файла. Это огорчает... Даже Мелкософтовский просмотрщик tif-ов (Imaging) не жрёт столько ресурсов, а это значит, что можно и эту проблему решить.
Надо дальше покопать.... smile.gif

Есть идея разбить изображение на части (количество зависит от размеров изображения и видимого пространства на форме), создать необходимое число CompatibleDC и записать в каждый из них свою часть рисунка. Потом смотреть какой из них виден и его выводить. Получается, что в худшем случае надо будет отобразить 4 части рисунка.... Вообщем это только идея, надо будет попробовать... если будет время (может и прдыдущее решение с отжиранием кучи памяти прокатит).
Guest
Отправлено: 17.03.2006, 22:37


Не зарегистрирован







Вопрос с памятью понятен. При работе с GDI+ такое действительно бывает, причем достаточно часто. Не знаю зачем, но разработчики Microsoft сделали GDI+ таким, что он рисует ВСЕГДА в режиме 24 бита. Неважно какая глубина цвета у исходного файла, какая глубина цвета в настоящий момент у экрана — работа с растрами происходит только после его преобразования в режим 24 бит.
Соответственно, если исходный файл имел 256 цветов (8 бит), и на диске занимал, например, 12МБ, то в памяти он будет занимать в (24/8)=3 раза больше, т.е. 36МБ.
Этот вопрос поднимался и на страницах статьи о работе с растрами в GDI+ (см. выше). Мало того, когда его задали разработчикам Microsoft, то ответ звучал примерно так: "Мы делали универсальный продукт. И он бесплатен. Не нравится — не используйте". (весьма вольный перевод).
К сожалению другого достаточно простого решения поставленной задачи на текущий момент я не нашел.
Grigoriy
Отправлено: 18.03.2006, 03:41


Мастер участка

Группа: Участник
Сообщений: 381



ну и правильно

Вернуться в Вопросы программирования в C++Builder