Есть такая чудная библиотека - ImageEn.
Мы ею пользуемся. Для многого. Связанного с графикой.
Руки дошли до её "вдумчивого тестирования". Ибо "жизнь заставила".
Так вот сложилось интересное наблюдение - если при помощи ImageEn мы из ОДНОГО и того же TBitmap делаем PNG (на диске скажем), то результат РАЗ ОТ РАЗУ - ОТЛИЧАЕТСЯ.
Даже ImageMagic со значением Fuzz в 20% "ловит отличия".
При этом если мы на этот TBitmap накладываем альфа-канал, то результат становится СТАБИЛЬНЫМ.
Обтрассировался их исходного кода (часть которого лишь в Obj-файлах), в чём дело - пока не понял.
Да и что характерно - файлы лишь "бинарно" отличаются, а вот "на глаз" - картинки ОДИНАКОВЫЕ.
Т.е. явно там какой-то "незначащий мусор". Может тот самый "недостающий" альфа-канал?
TPNGObject - ещё хуже себя ведёт.
Для ImageEn код такой:
А вот ТАК и PNG работает СТАБИЛЬНО (с ТОЧНОСТЬЮ до БАЙТА):
И ещё там в "кишках" Obj-файлов линкуется метод _malloc.
Который выглядел так:
Когда я его переписал так:
-- стало "чуть лучше".
Явно они где-то по памяти "гадят".
Что ещё хочу сказать? А банально.. У парней, которые писали ImageEn - ЯВНО НЕТУ ТЕСТОВ.
Даже АТОМАРНЫХ... Не говоря ух об интеграционных или модульных.
Ну и потом. Вот "это":
"Это", простите за резкое слово - "это" НЕ КОД, а говно.
Простите уж. Но Всё надо называть СВОИМИ ИМЕНАМИ.
"Это" - НЕ ЧЕЛОВЕКОЧИТАБЕЛЬНО.
А ещё кто-то "пенял мне" про "примеси".
Уж лучше "примеси", чем такое говно.
Есть ведь люди, которые ЯВНО обладают АНАЛИТИЧЕСКИМ и МАТЕМАТИЧЕСКИМ складом ума, но пишут говно-код (который в ЦЕЛОМ работает).
Я ВСЕГДА удивляюсь этому.
Потому, что я - РЕМЕСЛЕННИК.
Мы ею пользуемся. Для многого. Связанного с графикой.
Руки дошли до её "вдумчивого тестирования". Ибо "жизнь заставила".
Так вот сложилось интересное наблюдение - если при помощи ImageEn мы из ОДНОГО и того же TBitmap делаем PNG (на диске скажем), то результат РАЗ ОТ РАЗУ - ОТЛИЧАЕТСЯ.
Даже ImageMagic со значением Fuzz в 20% "ловит отличия".
При этом если мы на этот TBitmap накладываем альфа-канал, то результат становится СТАБИЛЬНЫМ.
Обтрассировался их исходного кода (часть которого лишь в Obj-файлах), в чём дело - пока не понял.
Да и что характерно - файлы лишь "бинарно" отличаются, а вот "на глаз" - картинки ОДИНАКОВЫЕ.
Т.е. явно там какой-то "незначащий мусор". Может тот самый "недостающий" альфа-канал?
TPNGObject - ещё хуже себя ведёт.
Для ImageEn код такой:
{$IfDef nsTest} l_Bmp.Bitmap.SaveToStream(l_Out); // - тут СТАБИЛЬНОЕ совпадение {$Else nsTest} l_IO := TImageEnIO.Create(nil); try //l_IO.AttachedBitmap := l_Bmp.Bitmap; l_IO.Bitmap := l_Bmp.Bitmap; l_IO.Params.BitsPerSample := 8; l_IO.Params.SamplesPerPixel := 1; {$IfDef nsTest} l_IO.SaveToStreamBMP(l_Out); // - тут ИНОГДА несовпадение с порогом в 3% {$Else nsTest} l_IO.SaveToStreamPNG(l_Out); // - тут ЧАСТО несовпадение с порогом даже БОЛЬШЕ 20% {$EndIf nsTest}Хотя ПОВТОРЮ - ВИЗУАЛЬНО отличий не видно.
А вот ТАК и PNG работает СТАБИЛЬНО (с ТОЧНОСТЬЮ до БАЙТА):
const c_ColWhite: TRGB = (b:255; g:255; r:255); ... l_Bitmap.SaveToStream(l_Stream); l_Stream.Seek(0, 0); with TImageEnIO.Create(nil) do try LoadFromStreamBMP(l_Stream); l_Stream.Seek(0, 0); l_Stream.Size:= 0; // создаём альфа-канал. потому что НАДО рисовать формулы // уже с альфа-каналом, IEBitmap.CreateAlphaChannel; for J := 0 to IEBitmap.Width - 1 do for K := 0 to IEBitmap.Height - 1 do if EqualRGB(IEBitmap.Pixels[J,K], c_ColWhite) then IEBitmap.Alpha[J,K] := 0 else IEBitmap.Alpha[J,K] := 255; SaveToStreamPNG(l_Stream);Да!
И ещё там в "кишках" Obj-файлов линкуется метод _malloc.
Который выглядел так:
function _malloc(aSize: Integer): Pointer; begin GetMem(Result, aSize); end;
Когда я его переписал так:
function _malloc(aSize: Integer): Pointer; begin GetMem(Result, aSize); if (Resut <> nil) then FillChar(Result, aSize, 0); end;
-- стало "чуть лучше".
Явно они где-то по памяти "гадят".
Что ещё хочу сказать? А банально.. У парней, которые писали ImageEn - ЯВНО НЕТУ ТЕСТОВ.
Даже АТОМАРНЫХ... Не говоря ух об интеграционных или модульных.
Ну и потом. Вот "это":
procedure WritePNGStream(Stream:TStream; bitmap:TIEBitmap; var IOParams:TIOParamsVals; var xProgress:TProgressRec; AlphaChannel:TIEMask); var png_ptr:png_structp; info_ptr:png_infop; Error_ptr:pointer; bit_depth,color_type,interlace_type:integer; WBitmap:TIEBitmap; BackCol,ForeCol:TRGB; FreeW:boolean; // se true liberare WBitmap qt:TIEQuantizer; palette:array [0..255] of TRGB; ppalette:PRGBROW; background:png_color_16; number_passes,pass,y,x,height,width:integer; px,ppx:pointer; pp:PRGB; brow:pbyte; pw:pword; nullpr:TProgressRec; bitmapwidth1:integer; iodata:TIOData; px2,px4:PRGBA; px_byte,px3:pbyte; bb:byte; bps:integer; hasalpha:boolean; px_word:pword; p8:png_color_8; l_SamplesPerByte: Byte; l_MaxColors: Integer; function FormRowByteWithPalette(var thePtr: PRGB; var theCounter: Integer): Byte; var l_Idx: Byte; I: Integer; begin Result := 0; for I := 1 to l_SamplesPerByte do begin Result := Result shl bit_depth; l_Idx := qt.RGBIndex[thePtr^]; Result := Result or l_Idx; Inc(thePtr); Inc(theCounter); end; end; begin with nullpr do begin Aborting:=xProgress.Aborting; fOnProgress:=nil; Sender:=nil; end; Error_ptr:=nil; png_ptr := png_create_write_struct('1.2.5',Error_ptr,@ErrorFunc, @WarnFunc); if png_ptr=nil then raise EInvalidGraphic.create('Error on creating PNG'); info_ptr := png_create_info_struct(png_ptr); if info_ptr=nil then begin png_destroy_write_struct(@png_ptr, nil); raise EInvalidGraphic.create('Error on creating PNG'); end; iodata.Stream:=Stream; iodata.Aborting:=xProgress.Aborting; png_set_write_fn(png_ptr,@iodata,@WriteFunc,@FlushFunc); // converts b/w bitmap to pf24bit regards of IOParams fields // The bitmap to write will be contained in WBitmap FreeW:=false; if (IOParams.BitsPerSample=1) then begin // required to save to b/w if Bitmap.pixelformat=ie1g then WBitmap:=Bitmap else begin // convert to 1 bit WBitmap:=_ConvertTo1bitEx(Bitmap,BackCol,ForeCol); if WBitmap=nil then begin // impossible to convert to 1 bit, convert to ordered dither WBitmap:=TIEBitmap.Create; WBitmap.Assign(Bitmap); WBitmap.PixelFormat:=ie1g; end; FreeW:=true; end; end else begin // required to save in true color if Bitmap.pixelformat=ie1g then begin // convert to 24 bit WBitmap:=TIEBitmap.Create; WBitmap.Assign(Bitmap); WBitmap.PixelFormat:=ie24RGB; FreeW:=true; end else WBitmap:=Bitmap; end; // assign interlace_type if ioparams.PNG_Interlaced then interlace_type:=PNG_INTERLACE_ADAM7 else interlace_type:=PNG_INTERLACE_NONE; // assign bit_depth and color_type if ioparams.SamplesPerPixel=1 then begin // B/W or palette if wbitmap.pixelformat=ie1g then begin // B/W color_type:=PNG_COLOR_TYPE_GRAY; bit_depth:=1; end else begin // palette color_type:=PNG_COLOR_TYPE_PALETTE; bit_depth:=ioparams.BitsPerSample; end; end else begin // true color color_type:=PNG_COLOR_TYPE_RGB; bit_depth:=8; end; hasalpha:=assigned(AlphaChannel) and (not AlphaChannel.Full); if hasalpha and (color_type=PNG_COLOR_TYPE_RGB) then color_type:=color_type or PNG_COLOR_MASK_ALPHA; // // Create palette if needed if (ioparams.SamplesPerPixel=1) and (ioparams.BitsPerSample>1) and (wbitmap.pixelformat<>ie1g) then begin if hasalpha then begin // save alpha channel as an entry in the color map (here 16 bit gray isn't supported) bit_depth:=8; bps:=1 shl ioparams.BitsPerSample; qt:=TIEQuantizer.Create(wBitmap, palette, imin(bps,255), -1, 0); // entry 0 reserved for transparent layer getmem(ppalette,256*sizeof(TRGB)); copymemory(@(ppalette[1]),@(palette[0]),255*sizeof(TRGB)); with ppalette[0] do begin r:=ioparams.PNG_Background.r; g:=ioparams.PNG_Background.g; b:=ioparams.PNG_Background.b; end; for x:=0 to 255 do bswap(ppalette^[x].r,ppalette^[x].b); png_set_PLTE(png_ptr,info_ptr,png_colorp(ppalette),imin(bps+1,256)); if hasalpha then begin bb:=0; png_set_tRNS(png_ptr,info_ptr,@bb,1,nil); end; end else begin // do not save alpha. Full gray and 16 bit gray supported l_MaxColors := 1 shl bit_depth; if l_MaxColors > 256 then l_MaxColors := 256; qt:=TIEQuantizer.Create(wBitmap, palette, l_MaxColors, -1, 0); getmem(ppalette,l_MaxColors*sizeof(TRGB)); copymemory(@(ppalette[0]),@(palette[0]),l_MaxColors*sizeof(TRGB)); for x:=0 to l_MaxColors-1 do bswap(ppalette^[x].r,ppalette^[x].b); if not qt.GrayScale then png_set_PLTE(png_ptr,info_ptr,png_colorp(ppalette),l_MaxColors) else color_type:=PNG_COLOR_TYPE_GRAY; end; end else begin qt:=nil; ppalette:=nil; end; png_set_IHDR(png_ptr,info_ptr,bitmap.width,bitmap.height,bit_depth,color_type, interlace_type,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); // DPI png_set_pHYs(png_ptr,info_ptr,round(ioparams.DPIX*100/2.54),round(ioparams.DPIY*100/2.54),PNG_RESOLUTION_METER); // case ioparams.PNG_Filter of ioPNG_FILTER_NONE : png_set_filter(png_ptr, 0, PNG_FILTER_NONE); ioPNG_FILTER_SUB : png_set_filter(png_ptr, 0, PNG_FILTER_SUB); ioPNG_FILTER_PAETH : png_set_filter(png_ptr, 0, PNG_FILTER_PAETH); end; // set background zeromemory(@background,sizeof(background)); if assigned(qt) then background.index:=qt.RGBIndex[ioparams.PNG_Background] else begin background.red:=ioparams.PNG_Background.r shl 8; background.green:=ioparams.PNG_Background.g shl 8; background.blue:=ioparams.PNG_Background.b shl 8; end; png_set_bKGD(png_ptr,info_ptr,@background); // png_set_compression_level(png_ptr,ioparams.PNG_Compression); // png_set_bgr(png_ptr); // png_write_info(png_ptr,info_ptr); // write rows number_passes := png_set_interlace_handling(png_ptr); height:=wbitmap.height; width:=wbitmap.width; xProgress.per1:=100/(height*number_passes); xProgress.val:=0; px2:=nil; if (color_type=PNG_COLOR_TYPE_PALETTE) or (color_type=PNG_COLOR_TYPE_GRAY) then getmem(px,wbitmap.width*imax(1,bit_depth div 8)); if (color_type and PNG_COLOR_MASK_ALPHA)<>0 then getmem(px2,wbitmap.width*sizeof(TRGBA)); l_SamplesPerByte := 8 div bit_depth; for pass := 0 to number_passes-1 do begin for y := 0 to height-1 do begin if (color_type and PNG_COLOR_MASK_PALETTE)<>0 then begin // palette brow:=px; pp:=wbitmap.scanline[y]; px_byte:=pbyte(pp); bitmapwidth1:=wbitmap.width-1; if assigned(AlphaChannel) and (not AlphaChannel.Full) then begin // alpha channel px3:=alphachannel.scanline[y]; for x:=0 to bitmapwidth1 do begin if px3^<255 data-blogger-escaped-1="" data-blogger-escaped-:="FormRowByteWithPalette(pp," data-blogger-escaped-and="" data-blogger-escaped-begin="" data-blogger-escaped-bitmapwidth1="" data-blogger-escaped-brow="" data-blogger-escaped-case="" data-blogger-escaped-color_type="" data-blogger-escaped-do="" data-blogger-escaped-else="" data-blogger-escaped-end="" data-blogger-escaped-for="" data-blogger-escaped-ie24rgb:="" data-blogger-escaped-ie8p:="" data-blogger-escaped-if="" data-blogger-escaped-inc="" data-blogger-escaped-of="" data-blogger-escaped-palette="" data-blogger-escaped-png_color_mask_color="" data-blogger-escaped-png_ptr="" data-blogger-escaped-png_write_rows="" data-blogger-escaped-pp="" data-blogger-escaped-px3="" data-blogger-escaped-px="" data-blogger-escaped-px_byte="" data-blogger-escaped-qt.rgbindex="" data-blogger-escaped-simple="" data-blogger-escaped-then="" data-blogger-escaped-to="" data-blogger-escaped-wbitmap.pixelformat="" data-blogger-escaped-while="" data-blogger-escaped-x:="0" data-blogger-escaped-x="">0 then begin // truecolor if (color_type and PNG_COLOR_MASK_ALPHA)<>0 then begin // alpha channel pp:=wbitmap.scanline[y]; px3:=alphachannel.scanline[y]; px4:=px2; for x:=0 to width-1 do begin with px4^ do begin r:=pp^.r; g:=pp^.g; b:=pp^.b; a:=px3^; end; inc(pp); inc(px3); inc(px4); end; png_write_rows(png_ptr, @px2, 1); end else begin ppx:=wbitmap.scanline[y]; png_write_rows(png_ptr, @ppx, 1); end; end else if (color_type=PNG_COLOR_TYPE_GRAY) then begin // gray scale if bit_depth=16 then begin case wbitmap.pixelformat of ie24RGB: begin pp:=wbitmap.scanline[y]; pw:=px; for x:=0 to width-1 do begin pw^:=qt.RGBIndex[pp^]; inc(pw); inc(pp); end; end; ie16g: begin px_word:=wbitmap.scanline[y]; pw:=px; for x:=0 to width-1 do begin pw^:=px_word^ shr 8; inc(pw); inc(px_word); end; end; end; png_write_rows(png_ptr, @px, 1); end else if bit_depth=8 then begin case wbitmap.pixelformat of ie24RGB: begin pp:=wbitmap.scanline[y]; brow:=px; for x:=0 to width-1 do begin brow^:=qt.RGBIndex[pp^]; inc(brow); inc(pp); end; end; ie8g: begin px_byte:=wbitmap.scanline[y]; brow:=px; for x:=0 to width-1 do begin brow^:=px_byte^; inc(brow); inc(px_byte); end; end; end; png_write_rows(png_ptr, @px, 1); end else begin if (bit_depth < 8) and ((wbitmap.pixelformat = ie24RGB)) then begin pp := wbitmap.scanline[y]; brow := px; x := 0; while x < width-1 do begin brow^ := FormRowByteWithPalette(pp, x); inc(brow); end; png_write_rows(png_ptr, @px, 1); end else begin ppx:=wbitmap.scanline[y]; png_write_rows(png_ptr, @ppx, 1); end; end; end; // OnProgress with xProgress do begin inc(val); if assigned(fOnProgress) then fOnProgress(Sender,trunc(per1*val)); end; if xProgress.Aborting^ then break; end; if xProgress.Aborting^ then break; end; if (color_type and PNG_COLOR_MASK_ALPHA)<>0 then freemem(px2); if (color_type=PNG_COLOR_TYPE_PALETTE) or (color_type=PNG_COLOR_TYPE_GRAY) then freemem(px); // fine if not xProgress.Aborting^ then png_write_end(png_ptr,info_ptr); if ppalette<>nil then freemem(ppalette); png_destroy_write_struct(@png_ptr,@info_ptr); if FreeW then WBitmap.free; if assigned(qt) then qt.free; end;
"Это", простите за резкое слово - "это" НЕ КОД, а говно.
Простите уж. Но Всё надо называть СВОИМИ ИМЕНАМИ.
"Это" - НЕ ЧЕЛОВЕКОЧИТАБЕЛЬНО.
А ещё кто-то "пенял мне" про "примеси".
Уж лучше "примеси", чем такое говно.
Есть ведь люди, которые ЯВНО обладают АНАЛИТИЧЕСКИМ и МАТЕМАТИЧЕСКИМ складом ума, но пишут говно-код (который в ЦЕЛОМ работает).
Я ВСЕГДА удивляюсь этому.
Потому, что я - РЕМЕСЛЕННИК.
Комментариев нет:
Отправить комментарий