-
Здравствуйте, хочу подключить к своей программе на Delphi бибиотеку libjpeg-turbo по аналогии как подключена libjpeg в модуле Jpeg.pas.
Кидаю в папку *.c и *.h файлы и пытаюсь собрать командой
SET PATH=d:\Langs\C++ Builder 5.5.1\Bin bcc32 -c -O2 -Oc -OS -d -w-par -w-aus -ff -pr -a4 -Ov -O -I..\..\tools\include *.c
Сыпет ошибками, подскажите как правильно собирать
-
Видимо не рассчитана она на сборку Билдером, это ж экзотика по нынешним временам, а 5.5 к тому же ещё и старая версия, SIMD-ассемблер вероятно не поймёт. Используй библиотеку как DLL.
-
Sapersky (09.02.14 19:26) [1] да, похоже что так, и либы готовые перевести coff2omf не вышло, буду тогда dll использовать
-
Кину сюда, может кому понадобится. dll нужна свежая, в которой экспортируются jpeg_mem_src и jpeg_mem_dest Декодирование и кодирование JPEG. Тестировал на D2010, как пример работы с jpeg_mem_src и jpeg_mem_dest unit suJpegTurboUnit;
interface
uses
Windows, SysUtils,
FastDIB;
type
TOnEncodedJpegBuffer = reference to procedure(ABuffer: Pointer; ABufferSize: LongWord);
function DecodeJpegTurbo(ABuffer: Pointer; ABufferLen: Integer; HQ: Boolean = True): TFastDIB;
procedure EncodeJpegTurbo(Source: TFastDIB; Quality: Integer; OnEncodedBuffer: TOnEncodedJpegBuffer);
implementation
uses
suJpegTurboHeadersUnit;
var
_LibInitialized: LongBool = False;
procedure ErrorExit(cinfo: j_common_ptr); cdecl;
var
Msg: AnsiString;
begin
SetLength(Msg, JMSG_LENGTH_MAX);
cinfo^.err^.format_message(cinfo, PAnsiChar(Msg));
raise Exception.CreateFmt('JPEG error #%d (%s)',
[cinfo^.err^.msg_code, PAnsiChar(Msg)]);
end;
procedure OutputMessage(cinfo: j_common_ptr); cdecl;
begin
end;
procedure InitLib;
begin
if _LibInitialized then
Exit;
if not init_libJPEG then
raise Exception.Create('initialization of libJPEG failed.');
if InterlockedCompareExchange(Integer(_LibInitialized), Integer(True), Integer(False)) = Integer(True) then
quit_libJPEG;
end;
function DecodeJpegTurbo(ABuffer: pointer; ABufferLen: Integer; HQ: Boolean): TFastDIB;
var
Loop: Integer;
JpegErr: jpeg_error_mgr;
Jpeg: jpeg_decompress_struct;
begin
InitLib;
FillChar(Jpeg, SizeOf(Jpeg), 0);
FillChar(JpegErr, SizeOf(JpegErr), 0);
jpeg_create_decompress(@Jpeg);
try
Jpeg.err := jpeg_std_error(@JpegErr);
JpegErr.error_exit := ErrorExit;
JpegErr.output_message := OutputMessage;
jpeg_mem_src(@Jpeg, ABuffer, ABufferLen);
jpeg_read_header(@jpeg, False);
jpeg.out_color_space := JCS_EXT_BGR;
jpeg.scale_num := 1;
jpeg.scale_denom := 1;
If HQ then
begin
jpeg.do_block_smoothing := 1;
jpeg.do_fancy_upsampling := 1;
jpeg.dct_method := JDCT_ISLOW
end
else
begin
jpeg.do_block_smoothing := 0;
jpeg.do_fancy_upsampling := 0;
jpeg.dct_method := JDCT_IFAST;
end;
jpeg_start_decompress(@Jpeg);
try
Result := TFastDIB.Create(jpeg.output_width, jpeg.output_height, 24);
try
for Loop := 0 to jpeg.output_height - 1 do
jpeg_read_scanlines(@jpeg, @Result.Scanlines[Result.Height - 1 - Loop], 1);
except
FreeAndNil(Result);
raise;
end;
finally
jpeg_finish_decompress(@Jpeg);
end;
finally
jpeg_destroy_decompress(@Jpeg);
end;
end;
procedure EncodeJpegTurbo(Source: TFastDIB; Quality: Integer; OnEncodedBuffer: TOnEncodedJpegBuffer);
var
ScanLine: JSAMPROW;
CompressedBuff: Pointer;
CompressedSize: LongWord;
JpegErr: jpeg_error_mgr;
Jpeg: jpeg_compress_struct;
begin
InitLib;
FillChar(Jpeg, SizeOf(Jpeg), 0);
FillChar(JpegErr, SizeOf(JpegErr), 0);
jpeg_create_compress(@Jpeg);
try
Jpeg.err := jpeg_std_error(@JpegErr);
JpegErr.error_exit := ErrorExit;
JpegErr.output_message := OutputMessage;
CompressedSize := 0;
CompressedBuff := nil;
jpeg_mem_dest(@Jpeg, @CompressedBuff, @CompressedSize);
jpeg.image_width := Source.Width;
jpeg.image_height := Source.Height;
jpeg.input_components := Source.Info.Header.BitCount div 8;
jpeg.in_color_space := JCS_EXT_BGR;
jpeg_set_defaults(@Jpeg);
jpeg_set_quality(@Jpeg, Quality, True);
jpeg_start_compress(@Jpeg, True);
try
while Jpeg.next_scanline < Jpeg.image_height do
begin
ScanLine := JSAMPROW(Source.Scanlines[Jpeg.image_height - Jpeg.next_scanline - 1]);
jpeg_write_scanlines(@Jpeg, @ScanLine, 1);
end;
finally
jpeg_finish_compress(@Jpeg);
end;
if Assigned(OnEncodedBuffer) then
OnEncodedBuffer(CompressedBuff, CompressedSize);
finally
jpeg_destroy_compress(@Jpeg);
end;
end;
initialization
finalization
if _LibInitialized then
quit_libJPEG;
end.
-
suJpegTurboHeadersUnit - это хидеры из https://code.google.com/p/delphi-libjpeg-turbo/ с парой дополнений (может что еще менял, не упомню):
J_COLOR_SPACE = (
JCS_UNKNOWN,
JCS_GRAYSCALE, JCS_RGB, JCS_YCbCr, JCS_CMYK, JCS_YCCK, JCS_EXT_RGB, JCS_EXT_RGBX, JCS_EXT_BGR, JCS_EXT_BGRX, JCS_EXT_XBGR, JCS_EXT_XRGB,
JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR, JCS_EXT_ARGB );
и это
jpeg_mem_src: procedure(cinfo: j_decompress_ptr; inbuffer: Pointer; insize: LongWord); cdecl;
jpeg_mem_dest: procedure(cinfo: j_decompress_ptr; outbuffer: Pointer; outsize: PLongWord); cdecl;
...
Function init_libJPEG(): boolean;
...
@jpeg_mem_src := GetProcAddress(libJPEG_Handle, 'jpeg_mem_src');
@jpeg_mem_dest := GetProcAddress(libJPEG_Handle, 'jpeg_mem_dest');
-
А ErrorExit нормально отрабатывает на битом jpeg или вообще не jpeg? У меня без хакерских извращений не хотело, и по документации я так понял, что это by design (родом из 90-х годов). Хотя может быть дело в try-except, которые я не догадался использовать, но теоретически древняя архитектура libjpeg не подразумевает исключений, разве что при ошибках в библиотеке. https://drive.google.com/file/d/0B_vHqwd58bsUNVpBa3BwSzVURGM/edit?usp=sharing
-
Ох ты ж какие хаки)
У меня работает, вылетает исключение с осмысленным текстом на битом файле. Может быть потому что у меня новая dll?
Код jpeg turbo не читал, так что теоретически могут быть следующие проблемы:
1. Если библиотека производит кодирование в отдельном потоке то исключение в наш поток не вернется. Но у меня работает все в одном потоке.
2. Если работа с выделением освобождением ресурсов в библиотеке организована без использования "try finally", т.е. расчитывает на коды ошибок, то мы своим raise можем нарушить работу и что-то не освободится внутри библиотеки.
Думаю проблемы в обоих случаях не будет, ведь разработчики должны были учитывать что может случиться AV или другое системное исключение, а наше ничем не хуже.
Я использовал dll из libjpeg-turbo-1.3.0-vc.exe.
А вообще выплевывать raise из ErrorExit это я подсмотрел в Jpeg.pas, там делается так же.
-
мы своим raise можем нарушить работу и что-то не освободится внутри библиотеки.Нет, не должны нарушить, она на то и рассчитана чтобы после error_exit "вывалиться" куда-нибудь: error_exit (j_common_ptr cinfo) Receives control for a fatal error. <...> Control must NOT return to the caller; generally this routine will exit() or longjmp() somewhere. http://apodeline.free.fr/DOC/libjpeg/libjpeg-3.html#ss3.4Так что исключения - нормальный вариант, просто я не догадался их применить и пришлось возиться с setjmp/longjmp. 1. Если библиотека производит кодирование в отдельном потоке то исключение в наш поток не вернется.Т.е. каждое исключение будет в своём потоке, как и коды ошибок? Так вроде вполне логично, я этого и ожидал.
-
Про Jump теперь понятно в чем логика)
-
Обнаружил неработоспособность под XP Prof SP2, нужны runtime библиотеки VC 2008. Пробовал подкладывать DLL (msvcr90.dll) - не работает, устанавливать vcredist посчитал что слишком жирно. В конечном итоге пересобрал lj-turbo со статической линковкой RTL, теперь работает, а размер по современным меркам почти не увеличился. Но только 32 бита, с 64 в VC Express много заморочек, пока не хочется возиться. https://drive.google.com/file/d/0B_vHqwd58bsUUDYyVUNVY3J0WGM/edit?usp=sharing
-
Спасибо!
-
Обнаружил большую утечку в EncodeJpegTurbo из [3], разбираюсь в чем дело.
-
jpeg_mem_dest что экспортируется из dll требует освобождения памяти из под картинки с помощью Free (подробнее в jdatadst.c), так как эта функция не экспортируется, пришлось делать свою реализацию оной процедуры:
unit suJpegTurboMemDestUnit;
interface
uses
suJpegTurboHeadersUnit;
procedure jpeg_mem_dest(cinfo: j_compress_ptr; outbuffer: PPointer; outsize: PLongWord);
implementation
const
OUTPUT_BUF_SIZE = 4096;
type
my_mem_destination_mgr = record
pub: jpeg_destination_mgr; outbuffer: PPointer; outsize: PLongWord;
newbuffer: Pointer; buffer: JOCTET_ptr; bufsize: LongWord;
end;
my_mem_dest_ptr = ^my_mem_destination_mgr;
procedure init_mem_destination(cinfo: j_compress_ptr); cdecl;
begin
end;
function empty_mem_output_buffer(cinfo: j_compress_ptr): Boolean; cdecl;
var
nextsize: LongWord;
dest: my_mem_dest_ptr;
nextbuffer: JOCTET_ptr;
begin
dest := my_mem_dest_ptr(cinfo^.dest);
nextsize := dest^.bufsize * 2;
nextbuffer := GetMemory(nextsize);
if nextbuffer = nil then
ERREXIT1(j_common_ptr(cinfo), JERR_OUT_OF_MEMORY, 10);
Move(dest^.buffer^, nextbuffer^, dest^.bufsize);
if dest^.newbuffer <> nil then
FreeMemory(dest^.newbuffer);
dest^.newbuffer := nextbuffer;
dest^.pub.next_output_byte := JOCTET_ptr(PByte(nextbuffer) + dest^.bufsize);
dest^.pub.free_in_buffer := dest^.bufsize;
dest^.buffer := nextbuffer;
dest^.bufsize := nextsize;
Result := True;
end;
procedure term_mem_destination(cinfo: j_compress_ptr); cdecl;
var
dest: my_mem_dest_ptr;
begin
dest := my_mem_dest_ptr(cinfo^.dest);
dest^.outbuffer^ := dest^.buffer;
dest^.outsize^ := dest^.bufsize - dest^.pub.free_in_buffer;
end;
procedure jpeg_mem_dest(cinfo: j_compress_ptr; outbuffer: PPointer; outsize: PLongWord);
var
dest: my_mem_dest_ptr;
begin
if (outbuffer = nil) or (outsize = nil) then
ERREXIT(j_common_ptr(cinfo), JERR_BUFFER_SIZE);
if (cinfo^.dest = nil) then cinfo^.dest := cinfo^.mem.alloc_small(j_common_ptr(cinfo), JPOOL_PERMANENT,
SizeOf(my_mem_destination_mgr));
dest := my_mem_dest_ptr(cinfo^.dest);
dest^.pub.init_destination := init_mem_destination;
dest^.pub.empty_output_buffer := empty_mem_output_buffer;
dest^.pub.term_destination := term_mem_destination;
dest^.outbuffer := outbuffer;
dest^.outsize := outsize;
dest^.newbuffer := nil;
if (outbuffer^ = nil) or (outsize^ = 0) then
begin
outbuffer^ := GetMemory(OUTPUT_BUF_SIZE);
dest^.newbuffer := outbuffer^;
if dest^.newbuffer = nil then
ERREXIT1(j_common_ptr(cinfo), JERR_OUT_OF_MEMORY, 10);
outsize^ := OUTPUT_BUF_SIZE;
end;
dest^.buffer := outbuffer^;
dest^.pub.next_output_byte := dest^.buffer;
dest^.bufsize := outsize^;
dest^.pub.free_in_buffer := dest^.bufsize;
end;
end.
-
Так же внесены изменения в suJpegTurboHeadersUnit: type
J_MESSAGE_CODE = (
JMSG_NOMESSAGE,
JERR_ARITH_NOTIMPL,
JERR_BAD_ALIGN_TYPE,
JERR_BAD_ALLOC_CHUNK,
JERR_BAD_BUFFER_MODE,
JERR_BAD_COMPONENT_ID,
JERR_BAD_CROP_SPEC,
JERR_BAD_DCT_COEF,
JERR_BAD_DCTSIZE,
JERR_BAD_DROP_SAMPLING,
JERR_BAD_HUFF_TABLE,
JERR_BAD_IN_COLORSPACE,
JERR_BAD_J_COLORSPACE,
JERR_BAD_LENGTH,
JERR_BAD_LIB_VERSION,
JERR_BAD_MCU_SIZE,
JERR_BAD_POOL_ID,
JERR_BAD_PRECISION,
JERR_BAD_PROGRESSION,
JERR_BAD_PROG_SCRIPT,
JERR_BAD_SAMPLING,
JERR_BAD_SCAN_SCRIPT,
JERR_BAD_STATE,
JERR_BAD_STRUCT_SIZE,
JERR_BAD_VIRTUAL_ACCESS,
JERR_BUFFER_SIZE,
JERR_CANT_SUSPEND,
JERR_CCIR601_NOTIMPL,
JERR_COMPONENT_COUNT,
JERR_CONVERSION_NOTIMPL,
JERR_DAC_INDEX,
JERR_DAC_VALUE,
JERR_DHT_INDEX,
JERR_DQT_INDEX,
JERR_EMPTY_IMAGE,
JERR_EMS_READ,
JERR_EMS_WRITE,
JERR_EOI_EXPECTED,
JERR_FILE_READ,
JERR_FILE_WRITE,
JERR_FRACT_SAMPLE_NOTIMPL,
JERR_HUFF_CLEN_OVERFLOW,
JERR_HUFF_MISSING_CODE,
JERR_IMAGE_TOO_BIG,
JERR_INPUT_EMPTY,
JERR_INPUT_EOF,
JERR_MISMATCHED_QUANT_TABLE,
JERR_MISSING_DATA,
JERR_MODE_CHANGE,
JERR_NOTIMPL,
JERR_NOT_COMPILED,
JERR_NO_ARITH_TABLE,
JERR_NO_BACKING_STORE,
JERR_NO_HUFF_TABLE,
JERR_NO_IMAGE,
JERR_NO_QUANT_TABLE,
JERR_NO_SOI,
JERR_OUT_OF_MEMORY,
JERR_QUANT_COMPONENTS,
JERR_QUANT_FEW_COLORS,
JERR_QUANT_MANY_COLORS,
JERR_SOF_DUPLICATE,
JERR_SOF_NO_SOS,
JERR_SOF_UNSUPPORTED,
JERR_SOI_DUPLICATE,
JERR_SOS_NO_SOF,
JERR_TFILE_CREATE,
JERR_TFILE_READ,
JERR_TFILE_SEEK,
JERR_TFILE_WRITE,
JERR_TOO_LITTLE_DATA,
JERR_UNKNOWN_MARKER,
JERR_VIRTUAL_BUG,
JERR_WIDTH_OVERFLOW,
JERR_XMS_READ,
JERR_XMS_WRITE
);
procedure ERREXIT(cinfo: j_common_ptr; code: J_MESSAGE_CODE);
procedure ERREXIT1(cinfo: j_common_ptr; code: J_MESSAGE_CODE; p1: Integer);
...
procedure ERREXIT(cinfo: j_common_ptr; code: J_MESSAGE_CODE);
begin
cinfo^.err^.msg_code := Ord(code);
cinfo^.err^.error_exit(j_common_ptr(cinfo));
end;
procedure ERREXIT1(cinfo: j_common_ptr; code: J_MESSAGE_CODE; p1: Integer);
begin
cinfo^.err^.msg_code := Ord(code);
cinfo^.err^.msg_parm.i[0] := p1;
cinfo^.err^.error_exit(j_common_ptr(cinfo));
end;
-
вопрос, в чем приимущества по сравнению со стандарным модулем?
-
Скорость сильно выше по кодированию и декодированию Jpg
-
тогда так: на сколько выше и для каких разрешений?
-
> brother © (23.05.14 10:33) [16] > тогда так: на сколько выше и для каких разрешений?
раза в 2-3 минимум
|