{
PixelGraphicPhotographicScaling реализует алгоритм масштабирования растровой
графики «билинейная интерполяция», который является стандартным.
Copyright © 2016–2017, 2019–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
общественной лицензии GNU.
Вы должны были получить копию Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit PixelGraphicPhotographicScaling;
{$MODE DELPHI}
{$ASMMODE INTEL}
interface
uses
Lang,
IntelMMX,
PixelGraphicScaling;
{%region public }
type
Algorithm = class(RefCountInterfacedObject, PixelGraphicScalingAlgorithm)
private
class function getAveragePixel(pixel0, pixel1: int): int; overload;
class function getAveragePixel(pixel00, pixel01, pixel10, pixel11: int): int; overload;
class function getAveragePixel1(pixel0, pixel1: int): int;
class function getAveragePixel2(pixel0, pixel1: int): int;
class function getAveragePixel11(pixel00, pixel01, pixel10, pixel11: int): int;
class function getAveragePixel12(pixel00, pixel01, pixel10, pixel11: int): int;
class function getAveragePixel21(pixel00, pixel01, pixel10, pixel11: int): int;
class function getAveragePixel22(pixel00, pixel01, pixel10, pixel11: int): int;
private
inp: PIntArray;
width: int;
height: int;
inScanline: int;
square3x3: int_Array1d;
public
constructor create();
procedure setSource(pixels: PIntArray; width, height, scanline: int);
procedure scale2x(outp: PIntArray; outScanline: int; turned: boolean);
procedure scale3x(outp: PIntArray; outScanline: int; turned: boolean);
procedure scale4x(outp: PIntArray; outScanline: int; turned: boolean);
function getSourceWidth(): int;
function getSourceHeight(): int;
end;
{%endregion}
implementation
{%region private }
var
REGBLANK: long = $0000000000000000;
CONST1D3: long = $0055005500550055;
CONST2D3: long = $00ab00ab00ab00ab;
CONST1D9: long = $001c001c001c001c;
CONST2D9: long = $0039003900390039;
CONST4D9: long = $0072007200720072;
{%endregion}
{%region Algorithm }
class function Algorithm.getAveragePixel(pixel0, pixel1: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
paddw mm0, mm1
psrlw mm0, $01
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel(pixel00, pixel01, pixel10, pixel11: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
movd mm2, [esp+$08]
movd mm3, [esp+$04]
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
punpcklbw mm2, [REGBLANK]
punpcklbw mm3, [REGBLANK]
paddw mm0, mm1
paddw mm0, mm2
paddw mm0, mm3
psrlw mm0, $02
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel1(pixel0, pixel1: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
pmullw mm0, [CONST2D3]
pmullw mm1, [CONST1D3]
paddw mm0, mm1
psrlw mm0, $08
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel2(pixel0, pixel1: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
pmullw mm0, [CONST1D3]
pmullw mm1, [CONST2D3]
paddw mm0, mm1
psrlw mm0, $08
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel11(pixel00, pixel01, pixel10, pixel11: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
movd mm2, [esp+$08]
movd mm3, [esp+$04]
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
punpcklbw mm2, [REGBLANK]
punpcklbw mm3, [REGBLANK]
pmullw mm0, [CONST4D9]
pmullw mm1, [CONST2D9]
pmullw mm2, [CONST2D9]
pmullw mm3, [CONST1D9]
paddw mm0, mm1
paddw mm0, mm2
paddw mm0, mm3
psrlw mm0, $08
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel12(pixel00, pixel01, pixel10, pixel11: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
movd mm2, [esp+$08]
movd mm3, [esp+$04]
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
punpcklbw mm2, [REGBLANK]
punpcklbw mm3, [REGBLANK]
pmullw mm0, [CONST2D9]
pmullw mm1, [CONST4D9]
pmullw mm2, [CONST1D9]
pmullw mm3, [CONST2D9]
paddw mm0, mm1
paddw mm0, mm2
paddw mm0, mm3
psrlw mm0, $08
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel21(pixel00, pixel01, pixel10, pixel11: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
movd mm2, [esp+$08]
movd mm3, [esp+$04]
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
punpcklbw mm2, [REGBLANK]
punpcklbw mm3, [REGBLANK]
pmullw mm0, [CONST2D9]
pmullw mm1, [CONST1D9]
pmullw mm2, [CONST4D9]
pmullw mm3, [CONST2D9]
paddw mm0, mm1
paddw mm0, mm2
paddw mm0, mm3
psrlw mm0, $08
packuswb mm0, mm0
movd eax, mm0
end;
class function Algorithm.getAveragePixel22(pixel00, pixel01, pixel10, pixel11: int): int; assembler; nostackframe;
asm
movd mm0, edx
movd mm1, ecx
movd mm2, [esp+$08]
movd mm3, [esp+$04]
punpcklbw mm0, [REGBLANK]
punpcklbw mm1, [REGBLANK]
punpcklbw mm2, [REGBLANK]
punpcklbw mm3, [REGBLANK]
pmullw mm0, [CONST1D9]
pmullw mm1, [CONST2D9]
pmullw mm2, [CONST2D9]
pmullw mm3, [CONST4D9]
paddw mm0, mm1
paddw mm0, mm2
paddw mm0, mm3
psrlw mm0, $08
packuswb mm0, mm0
movd eax, mm0
end;
constructor Algorithm.create();
begin
inherited create();
square3x3 := int_Array1d_create(3 * 3);
end;
procedure Algorithm.setSource(pixels: PIntArray; width, height, scanline: int);
begin
self.inp := pixels;
self.width := width;
self.height := height;
self.inScanline := scanline;
end;
procedure Algorithm.scale2x(outp: PIntArray; outScanline: int; turned: boolean);
var
inp: PIntArray;
inScanline: int;
inWidth: int;
inHeight: int;
inCurrLine: int;
inPixelIndex: int;
outHeight: int;
outCurrLine: int;
outPixelIndex: int;
i: int;
j: int;
pixel00: int;
pixel01: int;
pixel10: int;
pixel11: int;
begin
inp := self.inp;
inScanline := self.inScanline;
inWidth := self.width;
inHeight := self.height;
inCurrLine := 0;
outHeight := inHeight shl 1;
outCurrLine := 0;
if turned then begin
outCurrLine := outScanline * (outHeight - 1);
outScanline := -outScanline;
end;
for j := 0 to inHeight - 1 do begin
for i := 0 to inWidth - 1 do begin
inPixelIndex := inCurrLine + i;
outPixelIndex := outCurrLine + (i shl 1);
pixel00 := inp[inPixelIndex];
if i < inWidth - 1 then begin
pixel01 := inp[inPixelIndex + 1];
if j < inHeight - 1 then begin
pixel10 := inp[inPixelIndex + inScanline];
pixel11 := inp[inPixelIndex + inScanline + 1];
end else begin
pixel10 := pixel00;
pixel11 := pixel01;
end;
end else begin
pixel01 := pixel00;
if j < inHeight - 1 then begin
pixel10 := inp[inPixelIndex + inScanline];
end else begin
pixel10 := pixel00;
end;
pixel11 := pixel10;
end;
outp[outPixelIndex] := pixel00;
outp[outPixelIndex + 1] := getAveragePixel(pixel00, pixel01);
outp[outPixelIndex + outScanline] := getAveragePixel(pixel00, pixel10);
outp[outPixelIndex + outScanline + 1] := getAveragePixel(pixel00, pixel01, pixel10, pixel11);
end;
inc(inCurrLine, inScanline);
inc(outCurrLine, outScanline shl 1);
end;
returnToFPU();
end;
procedure Algorithm.scale3x(outp: PIntArray; outScanline: int; turned: boolean);
var
pixels: PIntArray;
width: int;
height: int;
inScanline: int;
pixCurrLine: int;
pixPrevLine: int;
pixNextLine: int;
outCurrLine: int;
outNext1Line: int;
outNext2Line: int;
outIndex: int;
i: int;
j: int;
p: int_Array1d;
begin
pixels := self.inp;
width := self.width;
height := self.height;
inScanline := self.inScanline;
p := square3x3;
pixCurrLine := 0;
outCurrLine := 0;
if turned then begin
outCurrLine := outScanline * (height * 3 - 1);
outScanline := -outScanline;
end;
for j := 0 to height - 1 do begin
if j > 0 then begin
pixPrevLine := pixCurrLine - inScanline;
end else begin
pixPrevLine := pixCurrLine;
end;
if j < height - 1 then begin
pixNextLine := pixCurrLine + inScanline;
end else begin
pixNextLine := pixCurrLine;
end;
outNext1Line := outCurrLine + outScanline;
outNext2Line := outNext1Line + outScanline;
outIndex := 0;
for i := 0 to width - 1 do begin
p[1] := pixels[pixPrevLine + i];
p[4] := pixels[pixCurrLine + i];
p[7] := pixels[pixNextLine + i];
if i > 0 then begin
p[0] := pixels[pixPrevLine + i - 1];
p[3] := pixels[pixCurrLine + i - 1];
p[6] := pixels[pixNextLine + i - 1];
end else begin
p[0] := p[1];
p[3] := p[4];
p[6] := p[7];
end;
if i < width - 1 then begin
p[2] := pixels[pixPrevLine + i + 1];
p[5] := pixels[pixCurrLine + i + 1];
p[8] := pixels[pixNextLine + i + 1];
end else begin
p[2] := p[1];
p[5] := p[4];
p[8] := p[7];
end;
{
p:
+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
| 3 | 4 | 5 |
+---+---+---+
| 6 | 7 | 8 |
+---+---+---+
}
{ Собственно сам алгоритм }
outp[outCurrLine + outIndex] := getAveragePixel22(p[0], p[1], p[3], p[4]);
outp[outCurrLine + outIndex + 1] := getAveragePixel2(p[1], p[4]);
outp[outCurrLine + outIndex + 2] := getAveragePixel21(p[1], p[2], p[4], p[5]);
outp[outNext1Line + outIndex] := getAveragePixel2(p[3], p[4]);
outp[outNext1Line + outIndex + 1] := p[4];
outp[outNext1Line + outIndex + 2] := getAveragePixel1(p[4], p[5]);
outp[outNext2Line + outIndex] := getAveragePixel12(p[3], p[4], p[6], p[7]);
outp[outNext2Line + outIndex + 1] := getAveragePixel1(p[4], p[7]);
outp[outNext2Line + outIndex + 2] := getAveragePixel11(p[4], p[5], p[7], p[8]);
{ Конец алгоритма }
inc(outIndex, 3);
end;
inc(pixCurrLine, inScanline);
inc(outCurrLine, outScanline * 3);
end;
end;
procedure Algorithm.scale4x(outp: PIntArray; outScanline: int; turned: boolean);
var
pixels: PIntArray;
width: int;
height: int;
inScanline: int;
pixCurrLine: int;
pixPrevLine: int;
pixNextLine: int;
outCurrLine: int;
outNext1Line: int;
outNext2Line: int;
outNext3Line: int;
outIndex: int;
i: int;
j: int;
p: int_Array1d;
begin
pixels := self.inp;
width := self.width;
height := self.height;
inScanline := self.inScanline;
p := square3x3;
pixCurrLine := 0;
outCurrLine := 0;
if turned then begin
outCurrLine := outScanline * (height * 4 - 1);
outScanline := -outScanline;
end;
for j := 0 to height - 1 do begin
if j > 0 then begin
pixPrevLine := pixCurrLine - inScanline;
end else begin
pixPrevLine := pixCurrLine;
end;
if j < height - 1 then begin
pixNextLine := pixCurrLine + inScanline;
end else begin
pixNextLine := pixCurrLine;
end;
outNext1Line := outCurrLine + outScanline;
outNext2Line := outNext1Line + outScanline;
outNext3Line := outNext2Line + outScanline;
outIndex := 0;
for i := 0 to width - 1 do begin
p[1] := pixels[pixPrevLine + i];
p[4] := pixels[pixCurrLine + i];
p[7] := pixels[pixNextLine + i];
if i > 0 then begin
p[0] := pixels[pixPrevLine + i - 1];
p[3] := pixels[pixCurrLine + i - 1];
p[6] := pixels[pixNextLine + i - 1];
end else begin
p[0] := p[1];
p[3] := p[4];
p[6] := p[7];
end;
if i < width - 1 then begin
p[2] := pixels[pixPrevLine + i + 1];
p[5] := pixels[pixCurrLine + i + 1];
p[8] := pixels[pixNextLine + i + 1];
end else begin
p[2] := p[1];
p[5] := p[4];
p[8] := p[7];
end;
{
p:
+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
| 3 | 4 | 5 |
+---+---+---+
| 6 | 7 | 8 |
+---+---+---+
}
{ Собственно сам алгоритм }
p[0] := getAveragePixel(p[0], p[1], p[3], p[4]);
p[2] := getAveragePixel(p[1], p[2], p[4], p[5]);
p[6] := getAveragePixel(p[3], p[4], p[6], p[7]);
p[8] := getAveragePixel(p[4], p[5], p[7], p[8]);
p[1] := getAveragePixel(p[1], p[4]);
p[3] := getAveragePixel(p[3], p[4]);
p[5] := getAveragePixel(p[4], p[5]);
p[7] := getAveragePixel(p[4], p[7]);
outp[outCurrLine + outIndex] := getAveragePixel(p[0], p[1], p[3], p[4]);
outp[outCurrLine + outIndex + 1] := getAveragePixel(p[1], p[4]);
outp[outCurrLine + outIndex + 2] := getAveragePixel(p[1], p[2], p[4], p[5]);
outp[outCurrLine + outIndex + 3] := getAveragePixel(p[2], p[5]);
outp[outNext1Line + outIndex] := getAveragePixel(p[3], p[4]);
outp[outNext1Line + outIndex + 1] := p[4];
outp[outNext1Line + outIndex + 2] := getAveragePixel(p[4], p[5]);
outp[outNext1Line + outIndex + 3] := p[5];
outp[outNext2Line + outIndex] := getAveragePixel(p[3], p[4], p[6], p[7]);
outp[outNext2Line + outIndex + 1] := getAveragePixel(p[4], p[7]);
outp[outNext2Line + outIndex + 2] := getAveragePixel(p[4], p[5], p[7], p[8]);
outp[outNext2Line + outIndex + 3] := getAveragePixel(p[5], p[8]);
outp[outNext3Line + outIndex] := getAveragePixel(p[6], p[7]);
outp[outNext3Line + outIndex + 1] := p[7];
outp[outNext3Line + outIndex + 2] := getAveragePixel(p[7], p[8]);
outp[outNext3Line + outIndex + 3] := p[8];
{ Конец алгоритма }
inc(outIndex, 4);
end;
inc(pixCurrLine, inScanline);
inc(outCurrLine, outScanline * 4);
end;
end;
function Algorithm.getSourceWidth(): int;
begin
result := width;
end;
function Algorithm.getSourceHeight(): int;
begin
result := height;
end;
{%endregion}
end.