Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Круглый стол
  
Правила КС
>> Настройки

Фильтр вопросов
>> Новые вопросы
отслеживать по
>> Новые ответы

Избранное

Страница вопросов
Поиск по КС


Специальные проекты:
>> К л ю к в а
>> Г о л о в о л о м к и

Вопрос №

Задать вопрос
Off-topic вопросы

Помощь

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  13:01[Войти] | [Зарегистрироваться]
Ответ на вопрос № 82040

24-06-2013 08:45
Доброго времени суток!
Хочу отобразить на форме исходный графический файл и результат его сжатия в JPEG с заданным качеством. За неимением лучшего, использую TJpegImage:

var
  bmp: TBitmap;
  jpg: TJpegImage;
begin
  bmp := TBitmap.Create;
  try
    bmp.LoadFromFile(FileName);
    Canvas.Draw(0, 0, bmp);
    jpg := TJpegImage.Create;
    try
      jpg.Assign(bmp);
      jpg.CompressionQuality := 10;
      jpg.Compress;
      jpg.DIBNeeded;
      Canvas.Draw(200, 0, jpg);
    finally
      FreeAndNil(jpg);
    end;
  finally
    FreeAndNil(bmp);
  end;
end;


Код написан исключительно для примера. Проблема в том, что рисуется несжатое изображение. Специально указал в коде CompressionQuality := 10, потому что результат такой компрессии не заметить трудно. DIBNeeded, соответственно, тоже не помогает. Если создать TMemoryStream, сохранить в него TJpegImage, а потом загрузить обратно, работает как надо. Исходников TJpegImage у меня нет. Можно ли как-то обойтись без сохранения / загрузки из потока? Или просто плюнуть на TJpegImage и использовать что-то другое (тогда посоветуйте — что)?

[+] Добавить в избранные вопросы

Отслеживать ответы на этот вопрос по RSS

Ответы:


Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице.
Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.

22-07-2013 15:22 | Сообщение от автора вопроса
В libjpeg-turbo появилась поддержка новых цветовых пространств, что позволяет не переставлять "вручную" байты, как это пришлось делать Sapersky в его примере, преобразуя декодированный libJPEG RGB в BGR TBitmap:


{$MINENUMSIZE 4} // Уже есть в оригинальном libJPEG.pas от Steffen Xonna
...
// New in libJPEG-turbo
{$DEFINE JCS_EXTENSIONS}
{$DEFINE JCS_ALPHA_EXTENSIONS}

  // jpeglib.h line 219
  J_COLOR_SPACE = (
    JCS_UNKNOWN,    { error/unspecified }
    JCS_GRAYSCALE,  { monochrome }
    JCS_RGB,        { red/green/blue }
    JCS_YCbCr,      { Y/Cb/Cr (also known as YUV) }
    JCS_CMYK,      { C/M/Y/K }
    JCS_YCCK        { Y/Cb/Cr/K }
    {$IFDEF JCS_EXTENSIONS},
    JCS_EXT_RGB,                     { red/green/blue }
    JCS_EXT_RGBX,                     { red/green/blue/x }
    JCS_EXT_BGR,                     { blue/green/red }
    JCS_EXT_BGRX,                     { blue/green/red/x }
    JCS_EXT_XBGR,                     { x/blue/green/red }
    JCS_EXT_XRGB                     { x/red/green/blue }
    {$IFDEF JCS_ALPHA_EXTENSIONS},
    { When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX,
      JCS_EXT_XBGR, or JCS_EXT_XRGB during decompression, the X byte is
      undefined, and in order to ensure the best performance,
      libjpeg-turbo can set that byte to whatever value it wishes.  Use
      the following colorspace constants to ensure that the X byte is set
      to 0xFF, so that it can be interpreted as an opaque alpha
      channel. }

    JCS_EXT_RGBA,                     { red/green/blue/alpha }
    JCS_EXT_BGRA,                     { blue/green/red/alpha }
    JCS_EXT_ABGR,                     { alpha/blue/green/red }
    JCS_EXT_ARGB                     { alpha/red/green/blue }
    {$ENDIF}
    {$ENDIF}
  );


С этим дополнением декодирование JPEG -> TBitmap у меня выполняется еще на ~24% быстрее, примерно в 5 раз быстрее стандартного TJPEGImage. Есть еще новые возможности, но их пока не реализовал. Подробнее в »вопрос КС №82087«. Если кому нужно, вот измененный заголовочник libJPEG_new.pas + новая версия DLL libjpeg-turbo 1.3: http://yadi.sk/d/d3LGDnFN76Y43

26-06-2013 08:34
>>>интересно, зачем так сделано?
Думаю, исходя из того, что jpeg-объект нужен в первую очередь как контейнер графики (т.е. для рисования его и в нем), и в этом плане он не отличается от битмапа, плюс по-простому отрисовывается функциями API. А все его отличие по части компресси проявляется только в файловых и потоковых операциях. То есть адекватное отображение с учетом настройки сжатия просто не входит в функции этого класса.

26-06-2013 08:26 | Сообщение от автора вопроса
Sapersky, большое спасибо! Это похоже на исчерпывающее решение проблемы. Сейчас разберусь, перепишу свой код. Еще раз спасибо!

Void, интересно, зачем так сделано? Что бы нельзя было выстрелить себе в ногу, десять раз вызвав метод Compress? :)

25-06-2013 15:08
libJpeg-turbo самая перспективная библиотека на данный момент. Не знаю как FastStone, но IrfanView её использует, например.
По скорости загрузки примерно соответствует IJL (т.е. в 2-3 раза быстрее стандартного модуля), но зато нет лицензионных заморочек, не нужен ключ в реестре (фактически права админа, чтобы его записать), лучше качество (IJL-ные картинки в быстром режиме отдают в зелень), есть 64-битный вариант.

Пример загрузки jpeg почти всеми возможными методами:
https://docs.google.com/file/d/0B_vHqwd58bsUYXQtYnVKV3B4RUE/edit?usp=sharing
Для libJpeg-turbo нужны модули libJPEG_IO (см. ReadJpeg/WriteJpeg) и libJPEG.
libJPEG_IO я слепил на основе примеров к libJPEG, но правда, пока мало тестировал (до сих пор использовал в основном IJL). Кое-что сделано криво и возможности реализованы не все. Версия Дельфи - от 4-й, наверное.

И кстати, на основе ReadJpeg/WriteJpeg можно написать функцию, которая будет выдавать картинку "после сжатия" без промежуточных буферов (точнее, с небольшим буфером длиной в строку).

24-06-2013 23:07
Судя по исходнику, внутри TJpegImage хранится обычный битмап, который используется при рисовании, причем он создается путем распаковки данных, если эти данные есть. При этом:
- при назначении через Assign битмап просто создается, если его еще нет, и используется дальше, сжатые данные вообще не трогаются;
- при обращении к битмапу (для рисования например) он создается или используется существующий или распаковывается из сжатых данных.
Таким образом, пока сжатые данные не загружены из файла, TJpegImage ведет себя как обычный TBitmap. Процедура же Compress просто берет данные из внутреннего битмапа и компрессирует их в сжатые данные, визуально ничего не меняя (используемый для рисования битмап как был так и остается несжатым).

24-06-2013 18:29
Спасибо за совет! Я, сдуру, вызывал Invalidate у PaintBox'а, который, естественно, приводил к очистке фона. При достаточно больших изображениях теперь тормозит само кодирование/декодирование в JPEG плюс выделение памяти при SaveToStram/LoadFromStream, но тут уже, видимо, нужно заменять TJPEGImage чем-то другим. У меня Delphi 7, модуль jpeg присутствует только в виде DCU.

24-06-2013 18:12
Если подытожить, то что я ниже написал по поводу обновления экрана, можно сказать так - ваше изображение должно храниться и обновляться в буфере, а окно программы - это зеркало, к-рое его отображает, но не хранит.

24-06-2013 18:08
По поводу моргания: чтобы избежать его, нужно ТОЛЬКО РИСОВАТЬ. Т.е. не стирать старое и рисовать новое, отсюда и мерцание (вы скажете - я не стираю, - операционная система стирает за вас), а рисовать новое прямо поверх старого. Это принцип, а реализация может быть разная. Например, юзаем TPaintBox. Во-первых, вешаем обработчик на его OnPaint, чтобы избежать ситуации, когда мы в данный момент ничего активно на нем не рисуем, и пользователь задвинет окно за экран, или свернет, а потом когда он развернет, то там ничего уже не будет. Всегда должен быть какой-то буфер (TBitmap), из к-рого в этом случае возьмется изображение. Во-вторых, когда мы что-то активно рисуем, то просто выводим поверх старого - PaintBox.Canvas.Draw. И в третьих - сами заводим свой буфер (TBitmap), а не используем DoubleBuffered, к-рое редко помогает, и его как параметр передаем в PaintBox.Canvas.Draw. Если нужно что-то нарисовать, то сначала рисуем в памяти, в буфере, а потом уже когда все готово, выводим его на экран методом канвы Draw.

По поводу TJpegImage, и его исходников. Они в Delphi есть, по крайней мере в моей 2010 версии. Посмотрел я на методы, когда вызываем SaveToStream, то там вызывается JPEGNeeded. Это так, для информации. В принципе отладчиком можно по цепочке пройтись, и станет понятно, что где происходит. Что я еще понял - внутри TJpegImage есть FBitmap, и его он наверняка и выводит на экран. Может быть проблема в том, что он не обновляется. По идее это должен делать DIBNeeded... короче пройтись с отладчиком там надо.

24-06-2013 17:22 | Сообщение от автора вопроса
В принципе, если при предпросмотре показывать еще и размер сжатого файла, SaveToStream все равно придется использовать. Напрягает то, что при таскании туда-сюда движка, задающего качество, перерисовка превьюшки ощутимо лагает. Пока сделал таймер, который запускается при изменении уровня компрессии и начинает увеличивать счетчик тиков. Каждое новое изменение уровня компрессии этот счетчик обнуляет. И только при достижении счетчиком определенного значения, происходит сжатие и перерисовка превьюшки. Но все равно моргает. DoubleBuffered не помогает. В принципе, можно, конечно, немного стуфтить и сжимать уменьшенную под размер превьюшки копию изображения, но это не совсем то...

24-06-2013 17:07
Вот и я тоже удивился — зачем нужен метод Compress и зачем опубликованы JPEGNeeded и DIBNeeded, если получается, что нет ни малейшего смысла их вызывать? Intel JPEG Library нашел, вот только не хотелось бы таскать с собой DLL. Библиотека FastDIB, вроде, для загрузки JPEG использует ее же. Еще наткнулся на libjpeg-turbo, но там, опять же, DLL и код несовместим со старыми версиями Delphi. Интересно, что использует FastStone Image Viewer (он написан на Delphi)?

24-06-2013 16:39
Сейчас попробовал, действительно не работает. Жаль исходники не сохранились, я когда-то делал программу, там была функция сохранения в JPEG, и был предпросмотр, но вот как я тогда это реализовал, я не помню :) Помню, что вроде все таки TJpegImage использовал. Хотя алтернативную библиотеку тоже как-то пробовал, не помню в той программе или просто ради интереса, есть Интеловская, довольно старая библиотека, к ней есть Дельфи интерфейс, если интересно, поищите со словами Intel Jpeg (library).

В справке написано: Use CompressionQuality to set the compression quality of the JPEG image when writing out a jpeg image. Тогда все правильно работает. Наверное я тогда в поток сохранял, в той моей программе. Зачем тогда метод Compress? Мда :)

Интересно как наш мозг устроен, вроде и не так много времени прошло, года три, а может пять, а попроси сейчас опять такую программу написать, наверное ровно столько же времени, чтобы разобраться, потратил бы :)

24-06-2013 16:20 | Сообщение от автора вопроса
Odelfer, спасибо за ответ. Максимальное качество у TJPEGImage при CompressionQuality = 100. Надо мне было вспомнить, что в некоторых приложениях качество JPEG задается от 1 до 10 и указать в вопросе, скажем, 12 или 20, чтобы не возникало путаницы. Попробовал поменять местами Assign и присваивание CompressionQuality. К сожалению, не помогло...

24-06-2013 15:56
1. С помощью TJpegImage вы сможете сделать все что вам нужно (все, что вы упоминали в вопросе, уж точно).
2. CompressionQuality := 10 это максимальное качество, как раз таки трудно будет заметить разницу.
3. jpg.DIBNeeded; вроде не нужен, я насколько помню, без него обходился.
4. Попробуйте поменять местами команды, сначала выставьте качество CompressionQuality, а потом уже вызывайте Assign. Кодирование по-моему как раз по Assign и происходит.

Извините, что все на пальцах, и без кода. Код не сохранился. Да и плохо помню уже, давно было, но использовал не раз TJpegImage, все должно работать, нужно просто правильно все делать.

Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
"Мы с тобой одной крови — ты и я!". Чьи это заветные слова?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.
Тип сообщения:
Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве

    Страница избранных вопросов Круглого стола.
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования