Обработка больших изображений в Delphi для сохранения в формате .jpeg

В Delphi 7 у меня есть библиотека, которая использует компонент TCanvas для вывода некоторой информации. Полученное изображение имеет размер около 4800 * 6000 пикселей, и я хотел бы распечатать его и сохранить как .jpeg.

Для этого я создал TBitmap и передал его Canvas в качестве параметра библиотеке, а затем назначил растровое изображение для jpeg. Очевидно, это занимает слишком много памяти, потому что я получаю исключение при попытке установить ширину и высоту растрового изображения, говоря: «Недостаточно памяти для обработки этой команды».

// output to printer
Printer.BeginDoc();
doPrint(Printer.Canvas);
Printer.EndDoc();

// output in bmp.Canvas
bmp := TBitmap.Create;
bmp.Width := Printer.PageWidth;
bmp.Height := Printer.PageHeight; // <- BAM! Exception!
doPrint(bmp.Canvas);

// save as jpeg
jpg := TJPEGImage.Create;
jpg.Assign(bmp);
jpg.SaveToFile('...');

// free
bmp.Free();
jpg.Free();

Что я делаю неправильно? Могу ли я сохранить Printer.Canvas непосредственно как .jpeg файл?

Изменить: обновлено приближение размера изображения с 2000 * 2000 до 4800 * 6000.


person Tom    schedule 28.10.2009    source источник
comment
Что это за библиотека, вы вынуждены использовать GETMEM или что-то подобное? Причина, по которой я спрашиваю, как будто это TBitmap, с вами все будет в порядке. RE   -  person Reallyethical    schedule 28.10.2009
comment
Я не использую ничего особенного, а библиотека - это просто то, что использует Canvas для вывода некоторой информации, она также написана на Delphi, и весь исходный код заполнен не чем иным, как var s: TStringList и cnv.TextOut (..) так что его можно легко перекомпилировать, и все должно быть в порядке.   -  person Tom    schedule 28.10.2009


Ответы (10)


вы должны иметь возможность обрабатывать большие растровые изображения, используя TBitmap32 из Graphic32 (http://www.graphics32.org/wiki/)

person glob    schedule 29.10.2009

Как предлагает Бен Зиглер, вы должны установить какой-либо формат пикселей для BMP, прежде чем изменять размер BMP. В этом вся разница.

person Niels Thomsen    schedule 29.10.2009
comment
Я тоже попробую, это все равно черно-белое изображение. - person Tom; 30.10.2009

Эта ошибка возникает, если вы устанавливаете размер растрового изображения больше, чем размер вашего рабочего стола. Чтобы избежать этой ошибки, вы можете создать растровое изображение, независимое от устройства, следующим образом:

bmp := TBitmap.Create;
bmp.HandleType := bmDIB;
bmp.Width := Printer.PageWidth;
bmp.Height := Printer.PageHeight; 

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

person GHN    schedule 25.09.2012

У класса TBitmap Delphi есть проблемы с обработкой таких больших растровых изображений. И нет, вы не можете сохранить TCanvas непосредственно в файл .jpg.

person Remy Lebeau    schedule 28.10.2009
comment
Если в ближайшее время никто не предложит никаких идей / обходных решений, я приму ваш ответ. - person Tom; 29.10.2009
comment
Сам класс TBitmap не имеет никаких проблем, это просто оболочка для функций Windows API. В зависимости от версии Windows и видеокарты (драйвера) могут быть проблемы или нет, просто так, но Delphi на самом деле не входит в это. Я могу без проблем создать в своей системе растровое изображение размером 10000 на 10000 пикселей. Просто нужно время, чтобы залить его цветом или сохранить в файл (размером 380 МБ). - person mghie; 29.10.2009

Я попробовал следующий код на своей машине (Windows XP, Delphi 2006) и не получил никаких исключений. Какую ОС вы используете?

  procedure TForm3.Button3Click(Sender: TObject);
  var
     bmp : TBitmap;
  begin
     bmp := TBitmap.Create;
     bmp.PixelFormat := pf32bit;
     bmp.Width := 6000;
     bmp.Height := 4800;
     bmp.Free;
  end;
person Ben Ziegler    schedule 28.10.2009
comment
Windows XP SP1 с 1 ГБ оперативной памяти. Приложение используется в Windows 7 с 2 ГБ ОЗУ без каких-либо проблем, но я хотел бы убедиться, что оно нормально работает в моей системе (по крайней мере, для отладки) - person Tom; 30.10.2009


Попробуйте установить PixelFormat на pf32bit или pf24bit (как в примере Бена Зиглера), в большинстве случаев этот PixelFormat помогает (насколько я помню, это было в основном на XP). Дополнительную информацию можно найти здесь.

person Krystian Bigaj    schedule 29.10.2009

Это не формат JPEG: спецификация допускает растровые изображения размером до 32767x32767 пикселей.

Проблема заключается в огромном потреблении памяти большими растровыми изображениями, и ограничениями TCanvas, которые в конечном итоге можно проследить до платформы Windows.

Моя библиотека NativeJpg декодирует обработку JPEG отдельно от визуализации, и вы можете, например, сохраните такой JPEG, используя «полосу за полосой», с более управляемыми фрагментами растрового изображения.

NativeJpg имеет открытый исходный код, и вы можете скачать эту библиотеку здесь: http://www.simdesign.nl/forum/viewforum.php?f=16

Посмотрите на tiledemo, чтобы узнать, как создавать и сохранять огромные искусственные изображения в формате JPEG.

С уважением, Нильс

person Nils Haeck    schedule 09.07.2011

Чтобы использовать меньше памяти, вы всегда можете попробовать создать растровые изображения меньшего размера. Допустим, вы разделите высоту принтера на 10 или установите максимальную высоту на 1000. Просто предложение, не уверен, применимо ли оно в вашем случае. Это приводит к более чем одному изображению на странице.

person Darkerstar    schedule 25.09.2012

Не уверен, что это сработает или поможет. Но мы создали функцию, которая сохранит компонент в jpeg:

    function SaveComponentToJPeg(mControl: TWinControl): TJpegImage;
    var
      bmp: TPicture;
      jpg : TJpegImage;
      dc: HDC;
      wnd: HWND;
      Params: array[0..255] of Char;

    begin
      bmp:=TPicture.Create;
      jpg := TJpegImage.create;
      try
        bmp.Bitmap.Width  := mControl.Width  - 05;  // Deduct for border.
        bmp.Bitmap.Height := mControl.Height -05;  // Deduct for border.
        wnd               := mControl.Handle;  //ctiveWindow;
        dc                := GetDc(wnd);
        BitBlt(bmp.Bitmap.Canvas.Handle,0,0,bmp.Width,bmp.Height,dc,0,0,SrcCopy);
        ReleaseDc(wnd, dc);
        jpg.assign(bmp.bitmap);
        result := jpg
      finally
        bmp.Free;
      end;
    end;
person M Schenkel    schedule 30.10.2009