{
    PixelGraphicSimpleScaling реализует алгоритм масштабирования растровой
    графики «простое масштабирование», который написал(а) Andrea Mazzoleni.

    Copyright © 2001–2004 Andrea Mazzoleni
    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 2 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit PixelGraphicSimpleScaling;

{$MODE DELPHI}

interface

uses
    Lang,
    PixelGraphicScaling;

{%region public }
type
    Algorithm = class(RefCountInterfacedObject, PixelGraphicScalingAlgorithm)
    private
        pixels16bit: short_Array1d;
        inp: PIntArray;
        width: int;
        height: int;
        inScanline: int;
        square3x3: int_Array1d;
        intermediate: short_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 Algorithm }
    constructor Algorithm.create();
    begin
        inherited create();
        square3x3 := int_Array1d_create(3 * 3);
    end;

    procedure Algorithm.setSource(pixels: PIntArray; width, height, scanline: int);
    begin
        self.pixels16bit := short_Array1d_create(width * height);
        self.inp := pixels;
        self.width := width;
        self.height := height;
        self.inScanline := scanline;
        self.intermediate := nil;
    end;

    procedure Algorithm.scale2x(outp: PIntArray; outScanline: int; turned: boolean);
    var
        pixels: short_Array1d;
        width: int;
        height: int;
        pixCurrLine: int;
        pixPrevLine: int;
        pixNextLine: int;
        outCurrLine: int;
        outNextLine: int;
        outIndex: int;
        i: int;
        j: int;
        p: int_Array1d;
    begin
        pixels := self.pixels16bit;
        width := self.width;
        height := self.height;
        transformTo16bit(self.inp, width, height, self.inScanline, pixels);
        p := square3x3;
        pixCurrLine := 0;
        outCurrLine := 0;
        if turned then begin
            outCurrLine := outScanline * (height * 2 - 1);
            outScanline := -outScanline;
        end;
        for j := 0 to height - 1 do begin
            if j > 0 then begin
                pixPrevLine := pixCurrLine - width;
            end else begin
                pixPrevLine := pixCurrLine;
            end;
            if j < height - 1 then begin
                pixNextLine := pixCurrLine + width;
            end else begin
                pixNextLine := pixCurrLine;
            end;
            outNextLine := outCurrLine + 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 |
                    +---+---+---+
                }
                { Собственно сам алгоритм }
                if (p[1] = p[3]) and (p[7] <> p[3]) and (p[1] <> p[5]) then begin
                    outp[outCurrLine + outIndex] := transformTo32bit(p[1]);
                end else begin
                    outp[outCurrLine + outIndex] := transformTo32bit(p[4]);
                end;
                if (p[1] = p[5]) and (p[1] <> p[3]) and (p[7] <> p[5]) then begin
                    outp[outCurrLine + outIndex + 1] := transformTo32bit(p[5]);
                end else begin
                    outp[outCurrLine + outIndex + 1] := transformTo32bit(p[4]);
                end;
                if (p[5] = p[7]) and (p[1] <> p[5]) and (p[3] <> p[7]) then begin
                    outp[outNextLine + outIndex + 1] := transformTo32bit(p[7]);
                end else begin
                    outp[outNextLine + outIndex + 1] := transformTo32bit(p[4]);
                end;
                if (p[3] = p[7]) and (p[5] <> p[7]) and (p[3] <> p[1]) then begin
                    outp[outNextLine + outIndex] := transformTo32bit(p[3]);
                end else begin
                    outp[outNextLine + outIndex] := transformTo32bit(p[4]);
                end;
                { Конец алгоритма }
                inc(outIndex, 2);
            end;
            inc(pixCurrLine, width);
            inc(outCurrLine, outScanline * 2);
        end;
    end;

    procedure Algorithm.scale3x(outp: PIntArray; outScanline: int; turned: boolean);
    var
        pixels: short_Array1d;
        width: int;
        height: 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.pixels16bit;
        width := self.width;
        height := self.height;
        transformTo16bit(self.inp, width, height, self.inScanline, pixels);
        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 - width;
            end else begin
                pixPrevLine := pixCurrLine;
            end;
            if j < height - 1 then begin
                pixNextLine := pixCurrLine + width;
            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 |
                    +---+---+---+
                }
                { Собственно сам алгоритм }
                if (p[1] = p[3]) and (p[3] <> p[7]) and (p[1] <> p[5]) then begin
                    outp[outCurrLine + outIndex] := transformTo32bit(p[3]);
                end else begin
                    outp[outCurrLine + outIndex] := transformTo32bit(p[4]);
                end;
                if ((p[1] = p[3]) and (p[3] <> p[7]) and (p[1] <> p[5]) and (p[4] <> p[2])) or ((p[1] = p[5]) and (p[1] <> p[3]) and (p[5] <> p[7]) and (p[4] <> p[0])) then begin
                    outp[outCurrLine + outIndex + 1] := transformTo32bit(p[1]);
                end else begin
                    outp[outCurrLine + outIndex + 1] := transformTo32bit(p[4]);
                end;
                if (p[1] = p[5]) and (p[3] <> p[1]) and (p[7] <> p[5]) then begin
                    outp[outCurrLine + outIndex + 2] := transformTo32bit(p[5]);
                end else begin
                    outp[outCurrLine + outIndex + 2] := transformTo32bit(p[4]);
                end;
                if ((p[7] = p[3]) and (p[5] <> p[7]) and (p[1] <> p[3]) and (p[4] <> p[0])) or ((p[1] = p[3]) and (p[7] <> p[3]) and (p[5] <> p[1]) and (p[4] <> p[6])) then begin
                    outp[outNext1Line + outIndex] := transformTo32bit(p[3]);
                end else begin
                    outp[outNext1Line + outIndex] := transformTo32bit(p[4]);
                end;
                outp[outNext1Line + outIndex + 1] := transformTo32bit(p[4]);
                if ((p[1] = p[5]) and (p[1] <> p[3]) and (p[5] <> p[7]) and (p[4] <> p[8])) or ((p[5] = p[7]) and (p[5] <> p[1]) and (p[3] <> p[7]) and (p[4] <> p[2])) then begin
                    outp[outNext1Line + outIndex + 2] := transformTo32bit(p[5]);
                end else begin
                    outp[outNext1Line + outIndex + 2] := transformTo32bit(p[4]);
                end;
                if (p[3] = p[7]) and (p[7] <> p[5]) and (p[1] <> p[3]) then begin
                    outp[outNext2Line + outIndex] := transformTo32bit(p[3]);
                end else begin
                    outp[outNext2Line + outIndex] := transformTo32bit(p[4]);
                end;
                if ((p[5] = p[7]) and (p[1] <> p[5]) and (p[3] <> p[7]) and (p[4] <> p[6])) or ((p[3] = p[7]) and (p[5] <> p[7]) and (p[1] <> p[3]) and (p[4] <> p[8])) then begin
                    outp[outNext2Line + outIndex + 1] := transformTo32bit(p[7]);
                end else begin
                    outp[outNext2Line + outIndex + 1] := transformTo32bit(p[4]);
                end;
                if (p[7] = p[5]) and (p[1] <> p[5]) and (p[7] <> p[3]) then begin
                    outp[outNext2Line + outIndex + 2] := transformTo32bit(p[5]);
                end else begin
                    outp[outNext2Line + outIndex + 2] := transformTo32bit(p[4]);
                end;
                { Конец алгоритма }
                inc(outIndex, 3);
            end;
            inc(pixCurrLine, width);
            inc(outCurrLine, outScanline * 3);
        end;
    end;

    procedure Algorithm.scale4x(outp: PIntArray; outScanline: int; turned: boolean);
    var
        pixels: short_Array1d;
        inter: short_Array1d;
        width: int;
        height: int;
        pixCurrLine: int;
        pixPrevLine: int;
        pixNextLine: int;
        outCurrLine: int;
        outNextLine: int;
        outIndex: int;
        i: int;
        j: int;
        p: int_Array1d;
    begin
        pixels := self.pixels16bit;
        inter := self.intermediate;
        width := self.width;
        height := self.height;
        if inter = nil then begin
            inter := short_Array1d_create(4 * width * height);
            self.intermediate := inter;
        end;
        transformTo16bit(self.inp, width, height, self.inScanline, pixels);
        p := square3x3;
        pixCurrLine := 0;
        outCurrLine := 0;
        for j := 0 to height - 1 do begin
            if j > 0 then begin
                pixPrevLine := pixCurrLine - width;
            end else begin
                pixPrevLine := pixCurrLine;
            end;
            if j < height - 1 then begin
                pixNextLine := pixCurrLine + width;
            end else begin
                pixNextLine := pixCurrLine;
            end;
            outNextLine := outCurrLine + width * 2;
            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 |
                    +---+---+---+
                }
                { Собственно сам алгоритм }
                if (p[1] = p[3]) and (p[7] <> p[3]) and (p[1] <> p[5]) then begin
                    inter[outCurrLine + outIndex] := p[1];
                end else begin
                    inter[outCurrLine + outIndex] := p[4];
                end;
                if (p[1] = p[5]) and (p[1] <> p[3]) and (p[7] <> p[5]) then begin
                    inter[outCurrLine + outIndex + 1] := p[5];
                end else begin
                    inter[outCurrLine + outIndex + 1] := p[4];
                end;
                if (p[5] = p[7]) and (p[1] <> p[5]) and (p[3] <> p[7]) then begin
                    inter[outNextLine + outIndex + 1] := p[7];
                end else begin
                    inter[outNextLine + outIndex + 1] := p[4];
                end;
                if (p[3] = p[7]) and (p[5] <> p[7]) and (p[3] <> p[1]) then begin
                    inter[outNextLine + outIndex] := p[3];
                end else begin
                    inter[outNextLine + outIndex] := p[4];
                end;
                { Конец алгоритма }
                inc(outIndex, 2);
            end;
            inc(pixCurrLine, width);
            inc(outCurrLine, width * 4);
        end;
        width := width shl 1;
        height := height shl 1;
        pixCurrLine := 0;
        outCurrLine := 0;
        if turned then begin
            outCurrLine := outScanline * (height * 2 - 1);
            outScanline := -outScanline;
        end;
        for j := 0 to height - 1 do begin
            if j > 0 then begin
                pixPrevLine := pixCurrLine - width;
            end else begin
                pixPrevLine := pixCurrLine;
            end;
            if j < height - 1 then begin
                pixNextLine := pixCurrLine + width;
            end else begin
                pixNextLine := pixCurrLine;
            end;
            outNextLine := outCurrLine + outScanline;
            outIndex := 0;
            for i := 0 to width - 1 do begin
                p[1] := inter[pixPrevLine + i];
                p[4] := inter[pixCurrLine + i];
                p[7] := inter[pixNextLine + i];
                if i > 0 then begin
                    {p[0] := inter[pixPrevLine + i - 1];}
                    p[3] := inter[pixCurrLine + i - 1];
                    {p[6] := inter[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] := inter[pixPrevLine + i + 1];}
                    p[5] := inter[pixCurrLine + i + 1];
                    {p[8] := inter[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 |
                    +---+---+---+
                }
                { Собственно сам алгоритм }
                if (p[1] = p[3]) and (p[7] <> p[3]) and (p[1] <> p[5]) then begin
                    outp[outCurrLine + outIndex] := transformTo32bit(p[1]);
                end else begin
                    outp[outCurrLine + outIndex] := transformTo32bit(p[4]);
                end;
                if (p[1] = p[5]) and (p[1] <> p[3]) and (p[7] <> p[5]) then begin
                    outp[outCurrLine + outIndex + 1] := transformTo32bit(p[5]);
                end else begin
                    outp[outCurrLine + outIndex + 1] := transformTo32bit(p[4]);
                end;
                if (p[5] = p[7]) and (p[1] <> p[5]) and (p[3] <> p[7]) then begin
                    outp[outNextLine + outIndex + 1] := transformTo32bit(p[7]);
                end else begin
                    outp[outNextLine + outIndex + 1] := transformTo32bit(p[4]);
                end;
                if (p[3] = p[7]) and (p[5] <> p[7]) and (p[3] <> p[1]) then begin
                    outp[outNextLine + outIndex] := transformTo32bit(p[3]);
                end else begin
                    outp[outNextLine + outIndex] := transformTo32bit(p[4]);
                end;
                { Конец алгоритма }
                inc(outIndex, 2);
            end;
            inc(pixCurrLine, width);
            inc(outCurrLine, outScanline * 2);
        end;
    end;

    function Algorithm.getSourceWidth(): int;
    begin
        result := width;
    end;

    function Algorithm.getSourceHeight(): int;
    begin
        result := height;
    end;
{%endregion}

end.

