![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
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...