Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2006.01.29;
Скачать: [xml.tar.bz2];

Вниз

StretchBlt на канву формы - искажение изображения   Найти похожие ветки 

 
DVM ©   (2005-12-26 12:25) [0]

Почему при выводе битмапа на канву формы через StretchBlt происходит искажение изображения - появляется какая то сетка (если рисовать надо меньше чем оригинал);


 
Eraser ©   (2005-12-26 12:33) [1]


> DVM ©   (26.12.05 12:25)

           SetStretchBltMode(Canvas.Handle, HALFTONE);
         // The docs say that you should call SetBrushOrgEx after SetStretchBltMode,
         // but not what the arguments should be.
           SetBrushOrgEx(Canvas.Handle, 0,0, nil);
           StretchBlt...


 
DVM ©   (2005-12-26 12:36) [2]


> Eraser ©   (26.12.05 12:33) [1]

Спасибо большое.


 
Deka ©   (2005-12-26 18:25) [3]

А еще можно посмотреть про функцию (не помню как называется) изменяющая Stretch-режим. Мне помнится удалось так победить искажения примерно в такой-же ситуации.


 
Eraser ©   (2005-12-26 18:29) [4]


> Deka ©   (26.12.05 18:25) [3]

Не SetStretchBltMode случайно назывется? )


 
GuAV ©   (2005-12-26 19:41) [5]

Немного оффтопик, но... никто "вручную" Stretch не делал ? А то StretchBlt игнорирует SetStretchBltMode в 9х (используется COLORONCOLOR), а хотелось бы BLACKONWHITE для одного случая...


 
Eraser ©   (2005-12-27 02:01) [6]


> GuAV ©   (26.12.05 19:41) [5]

Вручную не делал, но есть библиотеки, которые это умеют делать.


 
Deka ©   (2005-12-27 10:32) [7]

Да, именно так и называется (SetStrechBltMode). У меня есть процедура ручного strech, только называется это все ресамплингом, что есть правильно!
Попробуй разобраться...

unit Resample;
// -----------------------------------------------------------------------------
// Project: bitmap resampler
// Module: resample
// Description: Interpolated Bitmap Resampling using filters.
// Version: 01.02
// Release: 3
// Date: 15-MAR-1998
// Target: Win32, Delphi 2 & 3
// Author(s): anme: Anders Melander, anders@melander.dk
// Copyright (c) 1997,98 by Anders Melander
// Formatting: 2 space indent, 8 space tabs, 80 columns.
// -----------------------------------------------------------------------------
interface

uses
 SysUtils, Classes, Graphics;

type
 // Type of a filter for use with Stretch()
 TFilterProc = function(Value: Single): Single;

 // Sample filters for use with Stretch()
 function SplineFilter(Value: Single): Single;
 function BellFilter(Value: Single): Single;
 function TriangleFilter(Value: Single): Single;
 function BoxFilter(Value: Single): Single;
 function HermiteFilter(Value: Single): Single;
 function Lanczos3Filter(Value: Single): Single;
 function MitchellFilter(Value: Single): Single;

 // Interpolator
 // Src: Source bitmap
 // Dst: Destination bitmap
 // filter: Weight calculation filter
 // fwidth: Relative sample radius
 procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single);

// -----------------------------------------------------------------------------
//
//   List of Filters
//
// -----------------------------------------------------------------------------

const
 ResampleFilters: array[0..6] of record
   Name: string; // Filter name
   Filter: TFilterProc;// Filter implementation
   Width: Single; // Suggested sampling width/radius
 end = (
   (Name: "Box"; Filter: BoxFilter; Width: 0.5),    // 0
   (Name: "Triangle"; Filter: TriangleFilter; Width: 1.0),    // 1
   (Name: "Hermite"; Filter: HermiteFilter; Width: 1.0),    // 2
   (Name: "Bell"; Filter: BellFilter; Width: 1.5),    // 3
   (Name: "B-Spline"; Filter: SplineFilter; Width: 2.0),    // 4
   (Name: "Lanczos3"; Filter: Lanczos3Filter; Width: 3.0),    // 5
   (Name: "Mitchell"; Filter: MitchellFilter; Width: 2.0)     // 6
   );

                              IMPLEMENTATION

uses
 math, dialogs;

// -----------------------------------------------------------------------------
//
//   Filter functions
//
// -----------------------------------------------------------------------------

// Hermite filter
function HermiteFilter(Value: Single): Single;
begin
 // f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
 if (Value < 0.0) then Value := -Value;
 if (Value < 1.0) then Result := (2.0 * Value - 3.0) * Sqr(Value) + 1.0
                  else Result := 0.0;
end;

// Box filter
// a.k.a. "Nearest Neighbour" filter
// anme: I have not been able to get acceptable
//       results with this filter for subsampling.
function BoxFilter(Value: Single): Single;
begin
 if (Value > -0.5) and (Value <= 0.5) then Result := 1.0
                                      else Result := 0.0;
end;

// Triangle filter
// a.k.a. "Linear" or "Bilinear" filter
function TriangleFilter(Value: Single): Single;
begin
 if (Value < 0.0) then Value := -Value;
 if (Value < 1.0) then Result := 1.0 - Value
                  else Result := 0.0;
end;

// Bell filter
function BellFilter(Value: Single): Single;
begin
 if (Value < 0.0) then Value := -Value;
 if (Value < 0.5) then Result := 0.75 - Sqr(Value)
 else if (Value < 1.5) then begin
   Value := Value - 1.5;
   Result := 0.5 * Sqr(Value);
 end else
   Result := 0.0;
end;

// B-spline filter
function SplineFilter(Value: Single): Single;
var
 tt: single;
begin
 if (Value < 0.0) then Value := -Value;
 if (Value < 1.0) then begin
   tt := Sqr(Value);
   Result := 0.5*tt*Value - tt + 2.0 / 3.0;
 end else if (Value < 2.0) then
 begin
   Value := 2.0 - Value;
   Result := 1.0/6.0 * Sqr(Value) * Value;
 end else
   Result := 0.0;
end;

// Lanczos3 filter
function Lanczos3Filter(Value: Single): Single;
 function SinC(Value: Single): Single;
 begin
   if (Value <> 0.0) then begin
     Value := Value * Pi;
     Result := sin(Value) / Value
   end else
     Result := 1.0;
 end;
begin
 if (Value < 0.0) then Value := -Value;
 if (Value < 3.0) then Result := SinC(Value) * SinC(Value / 3.0)
                  else Result := 0.0;
end;

function MitchellFilter(Value: Single): Single;
const
 B = (1.0 / 3.0);
 C = (1.0 / 3.0);
var
 tt: single;
begin
 if (Value < 0.0) then Value := -Value;
 tt := Sqr(Value);
 if (Value < 1.0) then begin
   Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * tt))
     + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
     + (6.0 - 2 * B));
   Result := Value / 6.0;
 end else
 if (Value < 2.0) then begin
   Value := (((-1.0 * B - 6.0 * C) * (Value * tt))
     + ((6.0 * B + 30.0 * C) * tt)
     + ((-12.0 * B - 48.0 * C) * Value)
     + (8.0 * B + 24 * C));
   Result := Value / 6.0;
 end else
   Result := 0.0;
end;

// -----------------------------------------------------------------------------
//
//   Interpolator
//
// -----------------------------------------------------------------------------
type

 TCListList = array of record
   n: integer;
   p: array of record
        pixel : integer;  // Source pixel
        weight: single;  // Pixel weight
      end;
 end;

 TRGB = packed record
   r, g, b: single;
 end;

 // Physical bitmap pixel
 TColorRGB = packed record
   r, g, b: BYTE;
 end;
 PColorRGB = ^TColorRGB;

 // Physical bitmap scanline (row)
 TRGBList = packed array[0..0] of TColorRGB;
 PRGBList = ^TRGBList;


 
Deka ©   (2005-12-27 10:33) [8]

procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single);
var
 xscale, yscale : single;  // Zoom scale factors
 i, j, k  : integer;  // Loop variables
 center  : single;  // Filter calculation variables
 width, fscale, weight : single;  // Filter calculation variables
 left, right  : integer;  // Filter calculation variables
 n   : integer;  // Pixel number
 Work   : TBitmap;
 contrib  : TCListList;
 rgb   : TRGB;
 color   : TColorRGB;

 SourceLine  ,
 DestLine  : PRGBList;
//  SourcePixel  ,
 DestPixel  : PColorRGB;
 Delta   ,
 DestDelta  : integer;

 SrcWidth  ,
 SrcHeight  ,
 DstWidth  ,
 DstHeight  : integer;

 function Color2RGB(Color: TColor): TColorRGB;
 begin
   Result.r:= Color AND $000000FF;
   Result.g:= (Color AND $0000FF00) SHR 8;
   Result.b:= (Color AND $00FF0000) SHR 16;
 end;

 function RGB2Color(Color: TColorRGB): TColor;
 begin
   Result:= Color.r OR (Color.g SHL 8) OR (Color.b SHL 16);
 end;

begin
 DstWidth:= Dst.Width;             // Ширина приемника
 DstHeight:= Dst.Height;           // Высота приемника
 SrcWidth:= Src.Width;             // Ширина источника
 SrcHeight:= Src.Height;           // Высота источника

 if (SrcWidth < 1) or (SrcHeight < 1) then
   raise Exception.Create("Картинка-приемник слишком мала !");

 // Create intermediate image to hold horizontal zoom
 Work:= TBitmap.Create;            // Создали рабочую картинку

 try
   Work.Height:= SrcHeight;        // Высота рабочей картинки
   Work.Width:= DstWidth;          // Ширина рабочей картинки
   Work.PixelFormat:= pf24bit;     // Установили глубину цвета

   // xscale := DstWidth / SrcWidth;
   // yscale := DstHeight / SrcHeight;
   // Improvement suggested by David Ullrich:
   if (SrcWidth = 1) then xscale:= DstWidth / SrcWidth
                     else xscale:= (DstWidth - 1) / (SrcWidth - 1);
   if (SrcHeight = 1) then yscale:= DstHeight / SrcHeight
                      else yscale:= (DstHeight - 1) / (SrcHeight - 1);

   // This implementation only works on 24-bit images because it uses
   // TBitmap.Scanline

   If not (Src.PixelFormat=pf24bit) then ShowMessage("Проблема глубины цвета в источнике");
   If not (Dst.PixelFormat=pf24bit) then ShowMessage("Проблема глубины цвета в приемнике");

   // --------------------------------------------
   // Pre-calculate filter contributions for a row
   // -----------------------------------------------
   SetLength(contrib, DstWidth);
   // Horizontal sub-sampling - УМЕНЬШЕНИЕ
   // Scales from bigger to smaller width
   if (xscale < 1.0) then begin
     width:= fwidth / xscale;
     fscale:= 1.0 / xscale;
     for i:= 0 to DstWidth-1 do begin
       contrib[i].n:= 0;
       SetLength(contrib[i].p, trunc(width * 2.0 + 1));
       center:= i / xscale;

       // Original code:
       // left := ceil(center - width);
       // right := floor(center + width);
       left:= floor(center - width);   // floor - округление в меньшую сторону
       right:= ceil(center + width);   // ceil - округление в большую сторону

       for j:= left to right do begin
         weight:= filter((center - j) / fscale) / fscale;
         if (weight = 0.0) then continue;
         if (j < 0) then n:= -j else
           if (j >= SrcWidth) then n:= SrcWidth - j + SrcWidth - 1
                              else n:= j;
         k:= contrib[i].n;
         inc(contrib[i].n);
         contrib[i].p[k].pixel:= n;
         contrib[i].p[k].weight:= weight;
       end;
     end;
   end else
   // Horizontal super-sampling - УВЕЛИЧЕНИЕ
   // Scales from smaller to bigger width
   begin
     for i := 0 to DstWidth-1 do begin
       contrib[i].n:= 0;
       SetLength(contrib[i].p, trunc(fwidth * 2.0 + 1));
       center:= i/xscale;
       // Original code:
       // left := ceil(center - fwidth);
       // right := floor(center + fwidth);
       left:= floor(center - fwidth);
       right:= ceil(center + fwidth);
       for j:= left to right do begin
         weight:= filter(center - j);
         if (weight = 0.0) then continue;
         if (j < 0) then n:= -j else
           if (j >= SrcWidth) then n:= SrcWidth - j + SrcWidth - 1
                              else n:= j;
         k:= contrib[i].n;
         inc(contrib[i].n);
         contrib[i].p[k].pixel:= n;
         contrib[i].p[k].weight:= weight;
       end;
     end;
   end;


 
Deka ©   (2005-12-27 10:34) [9]

// ----------------------------------------------------
   // Apply filter to sample horizontally from Src to Work
   // ----------------------------------------------------
   for k:= 0 to SrcHeight-1 do begin
     SourceLine:= Src.ScanLine[k];        // Источник
     DestPixel:= Work.ScanLine[k];        // Приемник
     for i:= 0 to DstWidth-1 do begin
       rgb.r:= 0.0;
       rgb.g:= 0.0;
       rgb.b:= 0.0;

       for j:= 0 to contrib[i].n-1 do begin
         color:= SourceLine^[contrib[i].p[j].pixel];
         weight:= contrib[i].p[j].weight;
         if (weight = 0.0) then continue;
         rgb.r:= rgb.r + color.r * weight;
         rgb.g:= rgb.g + color.g * weight;
         rgb.b:= rgb.b + color.b * weight;
       end;

       if (rgb.r > 255.0) then color.r:= 255 else
         if (rgb.r < 0.0) then color.r:= 0
                          else color.r:= round(rgb.r);

       if (rgb.g > 255.0) then color.g:= 255 else
         if (rgb.g < 0.0) then color.g:= 0
                          else color.g:= round(rgb.g);

       if (rgb.b > 255.0) then color.b:= 255 else
         if (rgb.b < 0.0) then color.b:= 0
                          else color.b:= round(rgb.b);

       // Set new pixel value
       DestPixel^:= color;
       // Move on to next column
       inc(DestPixel);
     end;
   end;

   // Free the memory allocated for horizontal filter weights
   for i:= 0 to DstWidth-1 do Finalize(contrib[i].p);

   Finalize(contrib);

   // -----------------------------------------------
   // Pre-calculate filter contributions for a column
   // -----------------------------------------------
    SetLength(contrib, DstHeight);
   // Vertical sub-sampling - уменьшение
   // Scales from bigger to smaller height
   if (yscale < 1.0) then begin
     width:= fwidth / yscale;
     fscale:= 1.0 / yscale;
     for i:= 0 to DstHeight-1 do begin
       contrib[i].n:= 0;
       SetLength(contrib[i].p, trunc(width * 2.0 + 1));
       center:= i / yscale;
       // Original code:
       // left := ceil(center - width);
       // right := floor(center + width);
       left:= floor(center - width);
       right:= ceil(center + width);

       for j:= left to right do begin
         weight:= filter((center - j) / fscale) / fscale;
         if (weight = 0.0) then continue;
         if (j < 0) then n:= -j else
           if (j >= SrcHeight) then n:= SrcHeight - j + SrcHeight - 1
                               else n:= j;
         k:= contrib[i].n;
         inc(contrib[i].n);
         contrib[i].p[k].pixel:= n;
         contrib[i].p[k].weight:= weight;
       end;
     end
   end else
   // Vertical super-sampling
   // Scales from smaller to bigger height
   begin
     for i:= 0 to DstHeight-1 do begin
       contrib[i].n:= 0;
       SetLength(contrib[i].p, trunc(fwidth * 2.0 + 1));
       center:= i/yscale;
       // Original code:
       // left := ceil(center - fwidth);
       // right := floor(center + fwidth);
       left:= floor(center - fwidth);
       right:= ceil(center + fwidth);
       for j:= left to right do begin
         weight:= filter(center - j);
         if (weight = 0.0) then continue;
         if (j < 0) then n:= -j else
           if (j >= SrcHeight) then n:= SrcHeight - j + SrcHeight - 1
                               else n:= j;
         k:= contrib[i].n;
         inc(contrib[i].n);
         contrib[i].p[k].pixel:= n;
         contrib[i].p[k].weight:= weight;
       end;
     end;
   end;

   // --------------------------------------------------
   // Apply filter to sample vertically from Work to Dst
   // --------------------------------------------------
   SourceLine:= Work.ScanLine[0];
   Delta:= integer(Work.ScanLine[1]) - integer(SourceLine);
   DestLine:= Dst.ScanLine[0];
   DestDelta:= integer(Dst.ScanLine[1]) - integer(DestLine);
   for k:= 0 to DstWidth-1 do begin
     DestPixel:= pointer(DestLine);
     for i:= 0 to DstHeight-1 do begin
       rgb.r:= 0;
       rgb.g:= 0;
       rgb.b:= 0;
       // weight := 0.0;
       for j:= 0 to contrib[i].n-1 do begin
         color:= PColorRGB(integer(SourceLine)+contrib[i].p[j].pixel*Delta)^;
         weight:= contrib[i].p[j].weight;
         if (weight = 0.0) then continue;
         rgb.r:= rgb.r + color.r * weight;
         rgb.g:= rgb.g + color.g * weight;
         rgb.b:= rgb.b + color.b * weight;
       end;

       if (rgb.r > 255.0) then color.r:= 255 else
         if (rgb.r < 0.0) then color.r:= 0
                          else color.r:= round(rgb.r);

       if (rgb.g > 255.0) then color.g:= 255 else
         if (rgb.g < 0.0) then color.g:= 0
                          else color.g:= round(rgb.g);

       if (rgb.b > 255.0) then color.b:= 255 else
         if (rgb.b < 0.0) then color.b:= 0
                          else color.b:= round(rgb.b);

       DestPixel^:= color;
       inc(integer(DestPixel), DestDelta);
//        inc(DestPixel);
     end;
     Inc(SourceLine, 1);
     Inc(DestLine, 1);
   end;

   // Free the memory allocated for vertical filter weights
   for i:= 0 to DstHeight-1 do Finalize(contrib[i].p);

   Finalize(contrib);

 finally
   FreeAndNil(Work);
 end;
end;

end.


 
Deka ©   (2005-12-27 10:38) [10]

Как всешда есть место оптимизации... Удачи.

P.S. Основная идея ресамплинга сводится к тому, чтобы цветовую полоску (горизонтальную или вертикальную) превратить в аналоговую, а затем снова сделать дискретной с нужным количеством точек. Все работает в два прохода: горизонталь + вертикаль. Или наоборот - значения не имеет. И от того насколько качественно будет произведены операции преобразования в "аналоговое" изображение, а потом обратно зависит результат.


 
GuAV ©   (2005-12-27 21:51) [11]


> Deka ©

Спасибо.
Код вроде примерно понятен (хотя не выполнял) - сначала формируется массив индексируемый по номеру пискселя в строке, элементы которого - массивы из составляющих его в новом изображении пикселей и их весов, затем этот массив применяется к каждой строке добавлением пикселя с учётом веса.
Мне таки придётся поискать ещё или писать наново, т.к. у меня не требуется разнообразия фильтров и вообще сложного фильтра, нужно только "ближайший соседний" (причём минимум - BLACKONWHITE), но нужна максимальная производительность и желательна работа с меньшей глубиной цвета. Т.е. при этом же алгоритме мой массив contrib.p будет содержать только номера пикселей, при его применении будет браться минимум из тех пикселей, вещественночисленная арифметика не нужна.


 
Deka ©   (2005-12-28 13:27) [12]

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


 
app ©   (2005-12-28 13:38) [13]

Deka ©   (27.12.05 10:32) [7]
Для размещения больших кодов существуют разного рода кладовки, форум не кладовка.


 
Deka ©   (2005-12-28 16:06) [14]

Пардон.



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2006.01.29;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.042 c
15-1136506903
ronyn
2006-01-06 03:21
2006.01.29
Windows XP SP2 &amp; Bluetooth


1-1134906341
Kerk
2005-12-18 14:45
2006.01.29
одинаковые значения свойств


2-1136802445
RUBEY
2006-01-09 13:27
2006.01.29
Скроллинг по DBGRID кнопками


3-1133353334
alex_***
2005-11-30 15:22
2006.01.29
ORACLE и xml


15-1135690725
Holy
2005-12-27 16:38
2006.01.29
Новогодняя БМП (Belgorod MP)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский