Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

StretchBlt на канву формы - искажение изображения [D6, WinXP]


DVM ©   (2005-12-26 12:25:00) 

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


Deka   (2005-12-28 16:06:00) [1]

Пардон.


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

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


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

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


GuAV   (2005-12-27 21:51:00) [4]


> Deka ©

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


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

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

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


Deka   (2005-12-27 10:34:00) [6]

// ----------------------------------------------------
   // 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:33:00) [7]

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:32:00) [8]

Да, именно так и называется (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


Eraser   (2005-12-27 02:01:00) [9]


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

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


GuAV   (2005-12-26 19:41:00) [10]

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


Eraser   (2005-12-26 18:29:00) [11]


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

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


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

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


DVM   (2005-12-26 12:36:00) [13]


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

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


Eraser   (2005-12-26 12:33:00) [14]


> 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...


Наверх

Титульная страница Поиск, карта сайта Написать письмо