Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.01.29;
Скачать: CL | DM;

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.042 c
15-1136380135
Dasor
2006-01-04 16:08
2006.01.29
Помогите найти драйвер для мобилы


2-1135529412
b_o_b
2005-12-25 19:50
2006.01.29
paramcount


3-1132920032
VadimSpb
2005-11-25 15:00
2006.01.29
Копирование таблицы


15-1135925656
horse
2005-12-30 09:54
2006.01.29
Где играют в шахматы в сети?


15-1136742799
SergP
2006-01-08 20:53
2006.01.29
Приват24