{****************************************************************************}
{Unit VNM_JPG - it is a addon unit for graphics library VenomGFX.            }
{It bring a loader for .JPG graphics files                                   }
{      written by Michael Knapp, adjusted by Laaca                           }
{****************************************************************************}
unit vnm_JPG;
{$IFDEF VER2}{$DEFINE NEWFPC}{$ENDIF}
{$IFDEF VER3}{$DEFINE NEWFPC}{$ENDIF}
{$IFDEF NEWFPC}{$CALLING OLDFPCCALL}{$ENDIF}
{$ASMMODE INTEL}

{$R-}
interface
uses VenomGFX;


Function Load_JPG(s:string;var w:virtualwindow):byte;
{virtualwindow W will be initialized inside loader}


implementation
uses GRPfile,VenomMng,objects;


{========================== LoadImageJPG ==================================}

TYPE jpgfloat=single;
     Tblocklongint=packed array[0..7,0..7] of longint;

var costab:Tblocklongint;


  FUNCTION hi4(b:byte):byte;
  BEGIN
    hi4:=(b SHR 4) AND $0F;
  END;

  FUNCTION lo4(b:byte):byte;
  BEGIN
    lo4:=b AND $0F;
  END;

  PROCEDURE swap16(var p);assembler;
  ASM
  MOV EDI,p
  MOV AX,[EDI]
  XCHG AL,AH
  MOV [EDI],AX
  END;


  (*
  FUNCTION rgbcolor(f:longint):longint;assembler;
  ASM
  MOV EAX,f
  MOVZX BX,AH
  MOV AH,AL
  SHR EAX,8

  SHR AL,3
  AND AX,0F81Fh

  SHL BX,3
  AND BX,07E0h
  OR AX,BX
  AND EAX,0000FFFFh
  END;
  *)


{=== Annex A === Annex A === Annex A === Annex A === Annex A ===}


FUNCTION LoadImageJPG(stream:pstream;var img:VirtualWindow;nr:longint):longint;
const maxcomp=3;
      zigzag:packed array[0..7,0..7] of byte=
        (( 0, 1, 5, 6,14,15,27,28),
         ( 2, 4, 7,13,16,26,29,42),
         ( 3, 8,12,17,25,30,41,43),
         ( 9,11,18,24,31,40,44,53),
         (10,19,23,32,39,45,52,54),
         (20,22,33,38,46,51,55,60),
         (21,34,37,47,50,56,59,61),
         (35,36,48,49,57,58,62,63));

      zigizagi:packed array[0..63] of byte=
        (0, 8, 1, 2, 9, 16,24,17,
         10,3, 4, 11,18,25,32,40,
         33,26,19,12,5, 6, 13,20,
         27,34,41,48,56,49,42,35,
         28,21,14,7, 15,22,29,36,
         43,50,57,58,51,44,37,30,
         23,31,38,45,52,59,60,53,
         46,39,47,54,61,62,55,63);

        cuv=0.707106781;
        cxx:packed array[0..7] of jpgfloat=(cuv,1.0,1.0,1.0,1.0,1.0,1.0,1.0);
        fixpt=16;
        fixptmul=1 SHL fixpt;

type
     Tvectlongint=packed array[0..63] of longint;
     Pcoeffbuf=^Tcoeffbuf;
     Tcoeffbuf=packed array[0..0,0..63] of smallint;

var
    Qk:packed array[0..3] of tvectlongint;
    idctfix:packed array[0..1,0..1,1..3] of tblocklongint;
    pred:packed array[1..maxcomp] of longint;
    coeffbuf:packed array[1..maxcomp] of Pcoeffbuf;
    coeffbufW,coeffbufH,coeffbufidx,coeffeobrun,coeffX,coeffY,coeffbufXd,coeffbufYd:packed array[1..maxcomp] of longint;



  PROCEDURE invDCTfix(var dst,src:Tblocklongint);assembler;
  VAR rows:TBlockLongint;
  ASM
    {--- 1D-iDCT on rows ---}
    LEA EDI,rows
    MOV EDX,src
    MOV ECX,8
  @loop1a:
    PUSH ECX
    LEA ESI,costab

    MOV ECX,4
  @loop2a:
    PUSH ECX

    LODSD {0}
    IMUL EAX,DWORD PTR [EDX+000h]
    MOV EBX,EAX
    LODSD {1}
    IMUL EAX,DWORD PTR [EDX+004h]
    MOV ECX,EAX
    {----------}
    LODSD {2}
    IMUL EAX,DWORD PTR [EDX+008h]
    ADD EBX,EAX
    LODSD {3}
    IMUL EAX,DWORD PTR [EDX+00Ch]
    ADD ECX,EAX
    {----------}
    LODSD {4}
    IMUL EAX,DWORD PTR [EDX+010h]
    ADD EBX,EAX
    LODSD {5}
    IMUL EAX,DWORD PTR [EDX+014h]
    ADD ECX,EAX
    {----------}
    LODSD {6}
    IMUL EAX,DWORD PTR [EDX+018h]
    ADD EBX,EAX
    LODSD {7}
    IMUL EAX,DWORD PTR [EDX+01Ch]
    ADD ECX,EAX

    MOV EAX,EBX
    SUB EBX,ECX
    ADD EAX,ECX

    POP ECX

    MOV [EDI+ECX*8-4],EBX
    STOSD

    DEC ECX
    JNZ @loop2a

    POP ECX
    ADD EDI,16
    ADD EDX,020h
    DEC ECX
    JNZ @loop1a

    {--- 1D-iDCT on columns ---}
    MOV EDI,dst
    LEA ESI,costab

    MOV ECX,4
  @loop1b:
    PUSH ECX

    MOV EAX,ECX
    SHL EAX,6
    SUB EAX,36

    MOV ECX,8

    LEA EDX,rows

  @loop2b:
    PUSH ESI
    PUSH EAX
    PUSH ECX

    LODSD {0}
    IMUL EAX,DWORD PTR [EDX]
    MOV EBX,EAX
    LODSD {1}
    IMUL EAX,DWORD PTR [EDX+020h]
    MOV ECX,EAX
    {----------}
    LODSD {2}
    IMUL EAX,DWORD PTR [EDX+040h]
    ADD EBX,EAX
    LODSD {3}
    IMUL EAX,DWORD PTR [EDX+060h]
    ADD ECX,EAX
    {----------}
    LODSD {4}
    IMUL EAX,DWORD PTR [EDX+080h]
    ADD EBX,EAX
    LODSD {5}
    IMUL EAX,DWORD PTR [EDX+0A0h]
    ADD ECX,EAX
    {----------}
    LODSD {6}
    IMUL EAX,DWORD PTR [EDX+0C0h]
    ADD EBX,EAX
    LODSD {7}
    IMUL EAX,DWORD PTR [EDX+0E0h]
    ADD ECX,EAX

    MOV EAX,EBX
    SUB EBX,ECX
    ADD EAX,ECX

    POP ECX

    STOSD
    POP EAX

    MOV [EDI+EAX],EBX

    POP ESI
    ADD EDX,4
    DEC ECX
    JNZ @loop2b

    POP ECX
    ADD ESI,32
    DEC ECX
    JNZ @loop1b
  END;

{=== Annex C === Annex C === Annex C === Annex C === Annex C ===}

  TYPE phuff=^thuff;
       thuff=packed RECORD
         mincode,maxcode:array[1..16] of dword;
         valptr,bits:array[1..16] of longint;
         huffsize:array[0..256] of longint;
         huffval:array[0..256] of longint;
         huffcode:array[0..256] of dword;
         lastk:longint;
       END;
       phufftable=^thufftable;
       thufftable=packed array[0..1,0..3] of thuff;

  VAR hufftable:phufftable;

  TYPE
       DHT_t=packed RECORD
         Tc_Th:byte;
         Li:packed array[1..16] of byte;
         Vij:packed array[0..256] of byte;
       END;

       DQT_p=^DQT_t;
       DQT_t=packed RECORD
         Pq_Tq:byte;
         Qk:packed array[0..8*8-1] of smallint;
       END;

       SOF_t=packed RECORD
         P:byte;
         Y:word;
         X:word;
         Nf:byte;
         Ci:packed array[0..255] of byte;
         Hi_Vi:packed array[0..255] of byte;
         Tqi:packed array[0..255] of byte;
       END;

       SOS_t=packed RECORD
         Ns:byte;
         Csj:packed array[1..4] of byte;
         Tdj_Taj:packed array[1..10] of byte;
         Ss:byte;
         Se:byte;
         Ah_Al:byte;
       END;

  VAR
      DHT:DHT_t;
      DQT:DQT_t;
      SOF:SOF_t;
      SOS:SOS_t;
      SOFtype:word;

  PROCEDURE generate_size_table(tc,th:longint);
  VAR i,j,k:longint;
  BEGIN
    WITH hufftable^[tc,th] DO
      BEGIN
        k:=0;
        FOR i:=1 TO 16 DO
          BEGIN
            FOR j:=1 TO bits[i] DO
              BEGIN
                huffsize[k]:=i;
                inc(k);
              END;
          END;
        huffsize[k]:=0;
        lastk:=k;
      END;
  END;

  PROCEDURE generate_code_table(tc,th:longint);
  VAR k,si:longint;
      code:dword;
  BEGIN
    WITH hufftable^[tc,th] DO
      BEGIN
        k:=0;
        code:=0;
        si:=huffsize[0];
        REPEAT
          REPEAT
            huffcode[k]:=code;
{DBGw(hexlong(huffcode[k]));}
            code:=code+1;
            k:=k+1;
          UNTIL NOT (huffsize[k]=si);
          IF NOT (huffsize[k]=0) THEN
            BEGIN
              REPEAT
                code:=code SHL 1;
                si:=si+1;
              UNTIL (huffsize[k]=si);
            END;
        UNTIL (huffsize[k]=0);
      END;
  END;

  PROCEDURE Decoder_Tables(tc,th:longint);
  VAR i,j:longint;
  BEGIN
    WITH hufftable^[tc,th] DO
      BEGIN
        j:=0;
        FOR i:=1 TO 16 DO
          BEGIN
            IF (bits[i]=0) THEN
              BEGIN
                maxcode[i]:=$FFFFFFFF;
              END
            ELSE
              BEGIN
                valptr[i]:=j;
                mincode[i]:=huffcode[j];
                j:=j+bits[i]-1;
                maxcode[i]:=huffcode[j];
                j:=j+1;
              END;
          END;
      END;
  END;

{=== Annex F === Annex F === Annex F === Annex F === Annex F ===}

  VAR cnt_nb:longint;
      b_nb:byte;
      end_of_scan,end_of_image:boolean;
      restart_interval:longint;

  FUNCTION NEXTBYTE:byte;
  VAR b,c:byte;
  BEGIN
    stream^.read(b,1);
    IF (stream^.getpos>=stream^.getsize) THEN
      BEGIN
        end_of_scan:=TRUE;
        end_of_image:=TRUE;
      END
    ELSE
      BEGIN
        IF (b=$FF) THEN
          BEGIN
            stream^.read(c,1);
            IF (c<>0) THEN
              BEGIN
                stream^.seek(stream^.getpos-2);
                end_of_scan:=TRUE;
              END;
          END;
      END;
    NEXTBYTE:=b;
  END;

  FUNCTION NEXTBIT:longint;
  VAR bit:longint;
  BEGIN
    IF (cnt_nb=0) THEN
      BEGIN
        b_nb:=NEXTBYTE;
        cnt_nb:=8;
      END;
    bit:=b_nb SHR 7;
    cnt_nb:=cnt_nb-1;
    b_nb:=byte(b_nb SHL 1);
    NEXTBIT:=bit AND 1;
  END;

  FUNCTION RECEIVE(n:longint):longint;
  VAR bits:longint;
  BEGIN
    IF end_of_scan THEN exit;
    bits:=0;
    IF (n>=cnt_nb) THEN
      BEGIN
        bits:=b_nb SHR (8-cnt_nb);
        dec(n,cnt_nb);
        b_nb:=NEXTBYTE;
        cnt_nb:=8;
      END;
    WHILE (n>=8) DO
      BEGIN
        bits:=(bits SHL 8) OR b_nb;
        b_nb:=NEXTBYTE;
        dec(n,8);
      END;
    WHILE (n>0) DO
      BEGIN
        IF (cnt_nb=0) THEN
          BEGIN
            b_nb:=NEXTBYTE;
            cnt_nb:=8;
          END;
        bits:=(bits SHL 1) OR (b_nb SHR 7) AND 1;
        dec(cnt_nb);
        b_nb:=byte(b_nb SHL 1);
        dec(n);
      END;
    RECEIVE:=bits;
  END;

  FUNCTION EXTEND(v,t:longint):longint;assembler;
  ASM
    MOV ECX,t
    MOV EBX,1
    SHL EBX,CL
    SHR EBX,1
    MOV EAX,v
    CMP EAX,EBX
    JGE @nosign
    MOV EBX,-1
    SHL EBX,CL
    INC EBX
    ADD EAX,EBX
  @nosign:
  END;

{  FUNCTION RECEIVE(ssss:longint):longint;
  BEGIN
    RECEIVE:=NEXTBITS(ssss);
  END; }

  FUNCTION DECODE(tc,th:longint):longint;
  VAR i,j,value,code:dword;
  BEGIN
    WITH hufftable^[tc,th] DO
      BEGIN
        i:=1;
        code:=NEXTBIT;
        WHILE ((code>maxcode[i]) OR (bits[i]=0)) AND NOT(end_of_scan OR end_of_image) DO
          BEGIN
            i:=i+1;
            code:=(code SHL 1)+NEXTBIT;
          END;
        IF end_of_scan THEN exit;
        j:=valptr[i];
        j:=j+code-mincode[i];
        value:=huffval[j];
      END;
    DECODE:=value;
  END;

  PROCEDURE decode_coefficients_SOF0(sosidx,comp,v,h:longint);
  VAR i,k,rs,ssss,rrrr,t,hdc,hac:longint;
      coeff:array[0..63] of longint;
  {    dequ:Tblocklongint; }
      Q:^Tvectlongint;
  BEGIN
    IF end_of_scan THEN exit;
    hdc:=hi4(SOS.Tdj_Taj[sosidx]);
    hac:=lo4(SOS.Tdj_Taj[sosidx]);
    Q:=@Qk[SOF.Tqi[comp]];
    t:=DECODE(0,hdc);
    IF end_of_scan THEN exit;
    pred[comp]:=pred[comp]+EXTEND(RECEIVE(t),t);
    coeff[zigizagi[0]]:=pred[comp]*Q^[0];
    FOR i:=1 TO 63 DO coeff[i]:=0;
    k:=1;
    REPEAT
      rs:=DECODE(1,hac);
      ssss:=RS AND 15;
      rrrr:=(RS SHR 4) AND 15;
      k:=k+rrrr;
      IF (ssss<>0) THEN
        BEGIN
          IF (k<=63) THEN coeff[zigizagi[k]]:=EXTEND(RECEIVE(ssss),ssss)*Q^[k];
        END
      ELSE
        BEGIN
          IF (rrrr<>15) THEN break;
        END;
      inc(k);
    UNTIL (k>63) OR end_of_scan OR end_of_image;
   { _fixfastinvDCT(idctfix[v,h,comp],tblocklongint(coeff)); }
    invDCTfix(idctfix[v,h,comp],tblocklongint(coeff));
  END;

  PROCEDURE decode_coefficients_SOF2(sosidx,comp:longint);
  VAR k,rs,ssss,rrrr,t,hdc,hac,cb,eobrun,Al,Ah:longint;
      p1,m1:smallint;
      coeff:^smallint;
  BEGIN
    IF end_of_scan THEN exit;
    cb:=coeffbufidx[comp];
    eobrun:=coeffeobrun[comp];
    IF (cb>=coeffbufW[comp]*coeffbufH[comp]) THEN
      BEGIN
        WHILE NOT end_of_scan DO NEXTBIT;
        exit;
      END;
    Ah:=hi4(SOS.Ah_Al);
    Al:=lo4(SOS.Ah_Al);
    hdc:=hi4(SOS.Tdj_Taj[sosidx]);
    hac:=lo4(SOS.Tdj_Taj[sosidx]);
    IF (SOS.Ss=0) THEN
      BEGIN
        IF (Ah=0) THEN
          BEGIN
            t:=DECODE(0,hdc);
            pred[comp]:=pred[comp]+EXTEND(RECEIVE(t),t);
            IF end_of_scan THEN exit;
            coeffbuf[comp]^[cb,0]:=pred[comp] SHL Al;
          END
        ELSE
          BEGIN
            coeffbuf[comp]^[cb,0]:=coeffbuf[comp]^[cb,0] OR (receive(1) SHL Al);
          END;
      END
    ELSE
      BEGIN
        IF (Ah=0) THEN
          BEGIN
            {progessive}
            IF (eobrun>0) THEN
              BEGIN
                dec(eobrun);
              END
            ELSE
              BEGIN
                k:=SOS.Ss;
                WHILE (k<=SOS.Se) DO
                  BEGIN
                    rs:=DECODE(1,hac);
                    IF end_of_scan THEN exit;
                    ssss:=rs AND 15;
                    rrrr:=(rs SHR 4) AND 15;
                    IF (ssss<>0) THEN
                      BEGIN
                        inc(k,rrrr);
                        coeffbuf[comp]^[cb,k]:=EXTEND(RECEIVE(ssss),ssss) SHL Al;
                        IF end_of_scan THEN exit;
                      END
                    ELSE
                      BEGIN
                        IF (rrrr=15) THEN
                          BEGIN
                            inc(k,15);
                          END
                        ELSE
                          BEGIN
                            eobrun:=longint(1) SHL rrrr;
                            IF (rrrr<>0) THEN
                              BEGIN
                                rrrr:=RECEIVE(rrrr);
                                IF end_of_scan THEN exit;
                                inc(eobrun,rrrr);
                              END;
                            dec(eobrun);
                            break;
                          END;
                      END;
                    inc(k);
                  END;
              END;
          END
        ELSE
          BEGIN
            {successive}
            p1:=smallint(1) SHL Al;
            m1:=smallint(-1) SHL Al;
            k:=SOS.Ss;
            IF (eobrun=0) THEN
              BEGIN
                WHILE (k<=SOS.Se) DO
                  BEGIN
                    rs:=DECODE(1,hac);
                    IF end_of_scan THEN exit;
                    ssss:=rs AND 15;
                    rrrr:=(rs SHR 4) AND 15;
                    IF (ssss<>0) THEN
                      BEGIN
                        IF (RECEIVE(1)<>0) THEN ssss:=p1 ELSE ssss:=m1;
                        IF end_of_scan THEN exit;
                      END
                    ELSE
                      BEGIN
                        IF (rrrr<>15) THEN
                          BEGIN
                            eobrun:=longint(1) SHL rrrr;
                            IF (rrrr<>0) THEN
                              BEGIN
                                rrrr:=RECEIVE(rrrr);
                                IF end_of_scan THEN exit;
                                inc(eobrun,rrrr);
                              END;
                            break;
                          END;
                      END;
                    REPEAT
                      coeff:=@(coeffbuf[comp]^[cb,k]);
                      IF (coeff^<>0) THEN
                        BEGIN
                          IF (RECEIVE(1)<>0) THEN
                            IF (coeff^ AND p1=0) THEN
                              IF (coeff^>=0) THEN inc(coeff^,p1) ELSE inc(coeff^,m1);
                          IF end_of_scan THEN exit;
                        END
                      ELSE
                        BEGIN
                          dec(rrrr);
                          IF (rrrr<0) THEN break;
                        END;
                      inc(k);
                    UNTIL (k>SOS.Se);
                    IF (ssss<>0) THEN
                      BEGIN
                        coeffbuf[comp]^[cb,k]:=ssss;
                      END;
                    inc(k);
                  END;
              END;
            IF (eobrun>0) THEN
              BEGIN
                WHILE (k<=SOS.Se) DO
                  BEGIN
                    coeff:=@(coeffbuf[comp]^[cb,k]);
                    IF (coeff^<>0) THEN
                      BEGIN
                        IF (RECEIVE(1)<>0) THEN
                          IF (coeff^ AND p1=0) THEN
                            IF (coeff^>=0) THEN inc(coeff^,p1) ELSE inc(coeff^,m1);
                        IF end_of_scan THEN exit;
                      END;
                    inc(k);
                  END;
                dec(eobrun);
              END;
          END;
      END; { IF (SOS.Ss=0) THEN }
    coeffeobrun[comp]:=eobrun;
  END;

  FUNCTION ycbcr2rgbfix(y,cb,cr:longint):longint;assembler;
  CONST Rcr=trunc((1 SHL (32-fixpt))*1.40200);
        Gcb=trunc((1 SHL (32-fixpt))*0.34414);
        Gcr=trunc((1 SHL (32-fixpt))*0.71414);
        Bcb=trunc((1 SHL (32-fixpt))*1.77200);
  ASM
    MOV EBX,y
    SAR EBX,fixpt
    ADD EBX,128

    MOV EAX,Bcb
    IMUL cb
    ADD EDX,EBX
    TEST EDX,0FFFFFF00h
    SETZ AL
    SETS CL
    DEC AL
    OR DL,AL
    ADD DL,CL
    SHRD EDI,EDX,8

    MOV ECX,EBX
    MOV EAX,Gcr
    IMUL cr
    SUB ECX,EDX
    MOV EAX,Gcb
    IMUL cb
    SUB ECX,EDX
    TEST ECX,0FFFFFF00h
    SETZ AL
    SETS DL
    DEC AL
    OR CL,AL
    ADD CL,DL
    SHRD EDI,ECX,8

    MOV EAX,Rcr
    IMUL cr
    ADD EDX,EBX
    TEST EDX,0FFFFFF00h
    SETZ AL
    SETS CL
    DEC AL
    OR DL,AL
    ADD DL,CL
    SHRD EDI,EDX,8

    SHR EDI,8
    PUSH EDI
    CALL rgb32_16
  END;

  PROCEDURE draw;
  VAR x,y,c,v,h,v1,h1,v2,h2,v3,h3,hx,vy,xx,yy:longint;
  {    hi,vi:longint; }
      hh1,hh2,hh3:longint;
      vv1,vv2,vv3:longint;
      ih1,ih2,ih3:longint;
      iv1,iv2,iv3:longint;
      hx1,hx2,hx3:longint;
      vy1,vy2,vy3:longint;
  BEGIN
    xx:=coeffX[1];
    yy:=coeffY[1];
    v1:=lo4(SOF.Hi_Vi[1]);
    h1:=hi4(SOF.Hi_Vi[1]);
    v2:=lo4(SOF.Hi_Vi[2]);
    h2:=hi4(SOF.Hi_Vi[2]);
    v3:=lo4(SOF.Hi_Vi[3]);
    h3:=hi4(SOF.Hi_Vi[3]);
    IF (v1=0) THEN v1:=1;
    IF (h1=0) THEN h1:=1;
    IF (v2=0) THEN v2:=1;
    IF (h2=0) THEN h2:=1;
    IF (v3=0) THEN v3:=1;
    IF (h3=0) THEN h3:=1;
    ih1:=(h1 SHL 4) DIV h1;
    ih2:=(h2 SHL 4) DIV h1;
    ih3:=(h3 SHL 4) DIV h1;
    iv1:=(v1 SHL 4) DIV v1;
    iv2:=(v2 SHL 4) DIV v1;
    iv3:=(v3 SHL 4) DIV v1;
    FOR v:=0 TO v1-1 DO
      FOR h:=0 TO h1-1 DO
        BEGIN
          vv1:=v MOD v1;
          hh1:=h MOD h1;
          vv2:=v MOD v2;
          hh2:=h MOD h2;
          vv3:=v MOD v3;
          hh3:=h MOD h3;
          vy1:=(v*iv1) SHL 3;
          vy2:=(v*iv2) SHL 3;
          vy3:=(v*iv3) SHL 3;
          FOR y:=0 TO 7 DO
            BEGIN
              hx1:=(h*ih1) SHL 3;
              hx2:=(h*ih2) SHL 3;
              hx3:=(h*ih3) SHL 3;
              FOR x:=0 TO 7 DO
                BEGIN
                  CASE SOF.Nf OF
                  1:c:=ycbcr2rgbfix(idctfix[vv1,hh1,1][x,y],0,0);
                  3:c:=ycbcr2rgbfix(idctfix[vv1,hh1,1][(hx1 SHR 4) AND 7,(vy1 SHR 4) AND 7],
                                    idctfix[vv2,hh2,2][(hx2 SHR 4) AND 7,(vy2 SHR 4) AND 7],
                                    idctfix[vv3,hh3,3][(hx3 SHR 4) AND 7,(vy3 SHR 4) AND 7]);
                  END;
                  hx:=xx+(h SHL 3+x);
                  vy:=yy+(v SHL 3+y);
                  IF (hx<SOF.X) AND (vy<SOF.Y) THEN

                    {move(c,(img^.pixeldata+vy*img^.bytesperline+hx*bytperpix)^,bytperpix);}

                    Move(c,pointer(img.VWOffset+vy*img.bytebreite+hx*2)^,2);


                  inc(hx1,ih1);
                  inc(hx2,ih2);
                  inc(hx3,ih3);
                END;
              inc(vy1,iv1);
              inc(vy2,iv2);
              inc(vy3,iv3);
            END;
        END;
    inc(xx,(h1*8));
    IF (xx>=SOF.X) THEN
      BEGIN
        xx:=0;
        inc(yy,(v1*8));
        IF (yy>=SOF.Y) THEN
          BEGIN
            REPEAT
              NEXTBIT;
            UNTIL end_of_scan;
          END;
      END;
    coeffX[1]:=xx;
    coeffY[1]:=yy;
  END;

  PROCEDURE drawcoeffbuf;
  VAR xx,yy,x,y,c,v,h,v1,h1,v2,h2,v3,h3,hx,vy,hh,vv,i,j,k,l,qq:longint;
      dequ:tblocklongint;
   {   idct:array[0..1,0..1,1..maxcomp] of tblockfloat; }
      cbi,cbX,cbY:array[1..maxcomp] of longint;
      hh1,hh2,hh3:longint;
      vv1,vv2,vv3:longint;
      ih1,ih2,ih3:longint;
      iv1,iv2,iv3:longint;
      hx1,hx2,hx3:longint;
      vy1,vy2,vy3:longint;
  BEGIN
    v1:=lo4(SOF.Hi_Vi[1]);
    h1:=hi4(SOF.Hi_Vi[1]);
    v2:=lo4(SOF.Hi_Vi[2]);
    h2:=hi4(SOF.Hi_Vi[2]);
    v3:=lo4(SOF.Hi_Vi[3]);
    h3:=hi4(SOF.Hi_Vi[3]);
    IF (v1=0) THEN v1:=1;
    IF (h1=0) THEN h1:=1;
    IF (v2=0) THEN v2:=1;
    IF (h2=0) THEN h2:=1;
    IF (v3=0) THEN v3:=1;
    IF (h3=0) THEN h3:=1;
    ih1:=(h1 SHL 4) DIV h1;
    ih2:=(h2 SHL 4) DIV h1;
    ih3:=(h3 SHL 4) DIV h1;
    iv1:=(v1 SHL 4) DIV v1;
    iv2:=(v2 SHL 4) DIV v1;
    iv3:=(v3 SHL 4) DIV v1;
    xx:=0;
    yy:=0;
    FOR c:=1 TO SOF.Nf DO
      BEGIN
{DBG(cbi[c]); }
        cbi[c]:=0;
        cbX[c]:=0;
        cbY[c]:=0;
      END;
    REPEAT
      FOR c:=1 TO SOF.Nf DO
        BEGIN
          vv:=lo4(SOF.Hi_Vi[c]);
          hh:=hi4(SOF.Hi_Vi[c]);
          qq:=SOF.Tqi[c];
          FOR v:=0 TO vv-1 DO
            FOR h:=0 TO hh-1 DO
              BEGIN
                cbi[c]:=(cbY[c]+v)*coeffbufW[c]+(cbX[c]+h);
                l:=cbi[c];
                FOR i:=0 TO 7 DO
                  FOR j:=0 TO 7 DO
                    BEGIN
                      k:=zigzag[j,i];
                      dequ[i,j]:=coeffbuf[c]^[l][k]*Qk[qq][k];
                    END;
              {  _fixfastinvDCT(idctfix[v,h,c],dequ); }
                invDCTfix(idctfix[v,h,c],dequ);
                inc(cbi[c]);
              END;
          inc(cbX[c],hh);
          IF (cbX[c]>=coeffbufXd[c]) THEN
            BEGIN
              cbX[c]:=0;
              inc(cbY[c],vv);
            END;
        END;
      FOR v:=0 TO v1-1 DO
        FOR h:=0 TO h1-1 DO
          BEGIN
            vv1:=v MOD v1;
            hh1:=h MOD h1;
            vv2:=v MOD v2;
            hh2:=h MOD h2;
            vv3:=v MOD v3;
            hh3:=h MOD h3;
            vy1:=(v*iv1) SHL 3;
            vy2:=(v*iv2) SHL 3;
            vy3:=(v*iv3) SHL 3;
            FOR y:=0 TO 7 DO
              BEGIN
                hx1:=(h*ih1) SHL 3;
                hx2:=(h*ih2) SHL 3;
                hx3:=(h*ih3) SHL 3;
                FOR x:=0 TO 7 DO
                  BEGIN
                    CASE SOF.Nf OF
                    1:c:=ycbcr2rgbfix(idctfix[vv1,hh1,1][x,y],0,0);
                    3:c:=ycbcr2rgbfix(idctfix[vv1,hh1,1][(hx1 SHR 4) AND 7,(vy1 SHR 4) AND 7],
                                      idctfix[vv2,hh2,2][(hx2 SHR 4) AND 7,(vy2 SHR 4) AND 7],
                                      idctfix[vv3,hh3,3][(hx3 SHR 4) AND 7,(vy3 SHR 4) AND 7]);
                    END;
                    hx:=xx+(h SHL 3+x);
                    vy:=yy+(v SHL 3+y);
                    IF (hx<SOF.X) AND (vy<SOF.Y) THEN

                      Move(c,pointer(img.VWoffset+vy*img.bytebreite+hx*2)^,2);

                    inc(hx1,ih1);
                    inc(hx2,ih2);
                    inc(hx3,ih3);
                  END;
                inc(vy1,iv1);
                inc(vy2,iv2);
                inc(vy3,iv3);
              END;
          END;
  {    IF (xx=0) THEN ProgressMonitor(yy,SOF.Y); }
      {IF (xx=0) THEN ProgressMonitor(yy,(SOF.Y-1) AND NOT((v1*8)-1));}
      inc(xx,(h1*8));
      IF (xx>=SOF.X) THEN
        BEGIN
          xx:=0;
          inc(yy,(v1*8));
        END;
{putimage(0,0,img);
IF keypressed THEN IF readkey=#27 THEN halt; }
    UNTIL (yy>=SOF.Y);
{putimage(0,0,img);}
  END;

{=== Annex E === Annex E === Annex E === Annex E === Annex E ===}

{CONST mname:array[$FFC0..$FFFE] of string[5]=
      ('SOF0' ,'SOF1' ,'SOF2' ,'SOF3' ,'DHT'  ,'SOF5' ,'SOF6' ,'SOF7' ,
       'SOF8' ,'SOF9' ,'SOF10','SOF11','DAC'  ,'SOF13','SOF14','SOF15',
       'RST0' ,'RST1' ,'RST2' ,'RST3' ,'RST4' ,'RST5' ,'RST6' ,'RST7' ,
       'SOI'  ,'EOI'  , 'SOS' ,'DQT'  ,'DNL'  ,'DRI'  ,'DHP'  ,'EXP'  ,
       'APP0' ,'APP1' ,'APP2' ,'APP3' ,'APP4' ,'APP5' ,'APP6' ,'APP7' ,
       'APP8' ,'APP9' ,'APP10','APP11','APP12','APP13','APP14','APP15',
       'JPG0' ,'JPG1' ,'JPG2' ,'JPG3' ,'JPG4' ,'JPG5' ,'JPG6' ,'JPG7' ,
       'JPG8' ,'JPG9' ,'JPG10','JPG11','JPG12','JPG13','COM');
}
CONST
      mSOF0=$FFC0;
      mSOF1=$FFC1;
      mSOF2=$FFC2;
      mSOF3=$FFC3;
      mDHT=$FFC4;
      mSOF5=$FFC5;
      mSOF6=$FFC6;
      mSOF7=$FFC7;
      mSOF8=$FFC8;
      mSOF9=$FFC9;
      mSOF10=$FFCA;
      mSOF11=$FFCB;
      mDAC=$FFCC;
      mSOF13=$FFCD;
      mSOF14=$FFCE;
      mSOF15=$FFCF;
      mRST0=$FFD0;
      mRST1=$FFD1;
      mRST2=$FFD2;
      mRST3=$FFD3;
      mRST4=$FFD4;
      mRST5=$FFD5;
      mRST6=$FFD6;
      mRST7=$FFD7;
      mSOI=$FFD8;
      mEOI=$FFD9;
      mSOS=$FFDA;
      mDQT=$FFDB;
      mDNL=$FFDC;
      mDRI=$FFDD;
      mDHP=$FFDE;
      mEXP=$FFDF;
      mAPP0=$FFE0;
      mAPP1=$FFE1;
      mAPP2=$FFE2;
      mAPP3=$FFE3;
      mAPP4=$FFE4;
      mAPP5=$FFE5;
      mAPP6=$FFE6;
      mAPP7=$FFE7;
      mAPP8=$FFE8;
      mAPP9=$FFE9;
      mAPP10=$FFEA;
      mAPP11=$FFEB;
      mAPP12=$FFEC;
      mAPP13=$FFED;
      mAPP14=$FFEE;
      mAPP15=$FFEF;
      mJPG0=$FFF0;
      mJPG1=$FFF1;
      mJPG2=$FFF2;
      mJPG3=$FFF3;
      mJPG4=$FFF4;
      mJPG5=$FFF5;
      mJPG6=$FFF6;
      mJPG7=$FFF7;
      mJPG8=$FFF8;
      mJPG9=$FFF9;
      mJPG10=$FFFA;
      mJPG11=$FFFB;
      mJPG12=$FFFC;
      mJPG13=$FFFD;
      mCOM=$FFFE;

  FUNCTION readstream(n:longint):longint;
  VAR l:longint;
  BEGIN
    l:=0;
    stream^.read(l,n);
    readstream:=l;
  END;

  FUNCTION interpret_markers(marker:word):word;
  VAR segmarker,seglength:word;
      i,j,k,tc,th:longint;
  BEGIN
    interpret_markers:=0;
    IF end_of_image THEN exit;
    stream^.read(segmarker,2);
    swap16(segmarker);
    WHILE NOT((segmarker>=$FFC0) AND (segmarker<=$FFFE)) AND (stream^.getpos<stream^.getsize) DO
      BEGIN
        stream^.seek(stream^.getpos-1);
        stream^.read(segmarker,2);
        swap16(segmarker);
      END;
    interpret_markers:=segmarker;
    IF (stream^.getpos>=stream^.getsize) THEN
      BEGIN
        end_of_scan:=TRUE;
        end_of_image:=TRUE;
        exit;
      END;
{IF ((segmarker>=$FFC0) AND (segmarker<=$FFFE)) THEN
DBG('#'+mname[segmarker]) ELSE DBGw(hexword(segmarker)); }
    IF (marker<>0) THEN IF (segmarker<>marker) THEN exit;

    CASE segmarker OF
    mSOF0..mSOF3,mSOF5..mSOF11,mSOF13..mSOF15:
      BEGIN
{DBG(mname[segmarker]);}
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        stream^.read(SOF.P,1);
        stream^.read(SOF.Y,2);
        stream^.read(SOF.X,2);
        stream^.read(SOF.Nf,1);
        dec(seglength,6);
        FOR i:=1 TO SOF.Nf DO
          BEGIN
            stream^.read(SOF.Ci[i],1);
            stream^.read(SOF.Hi_Vi[i],1);
            stream^.read(SOF.Tqi[i],1);
            dec(seglength,3)
          END;
{DBG(seglength);}
        stream^.seek(stream^.getpos+seglength);
        swap16(SOF.Y);
        swap16(SOF.X);
        IF (segmarker>=mSOF0) AND (segmarker<=mSOF2) THEN
          BEGIN

            Init_VW(img,sof.x,sof.y,false);

            LoadImageJPG:=0;
            SOFtype:=segmarker;
          END
        ELSE
          BEGIN
            end_of_scan:=TRUE;
            end_of_image:=TRUE;
          END;
{DBG('  Nf',SOF.Nf);
FOR i:=1 TO SOF.Nf DO
  BEGIN
    DBG('  #idx',i);
    DBG('  Ci',SOF.Ci[i]);
    DBG('  Hi',hi4(SOF.Hi_Vi[i]));
    DBG('  Vi',lo4(SOF.Hi_Vi[i]));
  END;
DBG('  Y',SOF.y);
DBG('  X',SOF.x); }
      END;
    mDHT: {FFC4}
      BEGIN
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        WHILE (seglength>0) DO
          BEGIN
            stream^.read(DHT.Tc_Th,1);
            stream^.read(DHT.Li,16);
            dec(seglength,17);
            tc:=hi4(DHT.Tc_Th);
            th:=lo4(DHT.Tc_Th);
{DBG(tc);
DBG(th);}
            k:=0;
            FOR i:=1 TO 16 DO
              BEGIN
                hufftable^[tc,th].bits[i]:=DHT.Li[i];
                FOR j:=1 TO DHT.Li[i] DO
                  BEGIN
                    hufftable^[tc,th].huffval[k]:=readstream(1);
                    dec(seglength);
                    inc(k);
                  END;
              END;
{DBG(k);}
            generate_size_table(tc,th);
            generate_code_table(tc,th);
            decoder_tables(tc,th);
          END;
        stream^.seek(stream^.getpos+seglength);
      END;
    mSOI: {FFD8}
      BEGIN
      END;
    mEOI: {FFD9}
      BEGIN
        end_of_scan:=TRUE;
        end_of_image:=TRUE;
      END;
    mSOS: {FFDA}
      BEGIN
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        stream^.read(SOS.Ns,1);
        dec(seglength);
        FOR i:=1 TO SOS.Ns DO
          BEGIN
            stream^.read(SOS.Csj[i],1);
            stream^.read(SOS.Tdj_Taj[i],1);
{DBG('['+long2str(i)+']',SOS.Tdj_Taj[i]);
DBG(SOS.Csj[i]);}
            dec(seglength,2);
            IF (SOS.Csj[i]>SOF.Nf) THEN SOS.Csj[i]:=1;
          END;
        stream^.read(SOS.Ss,1);
        stream^.read(SOS.Se,1);
        stream^.read(SOS.Ah_Al,1);
        dec(seglength,3);
        stream^.seek(stream^.getpos+seglength);
{DBG('  Ns',SOS.Ns);
DBG('  Ss',SOS.Ss);
DBG('  Se',SOS.Se);
DBG('  Al',lo4(SOS.Ah_Al));
DBG('  Ah',hi4(SOS.Ah_Al));
DBG('============'); }
      END;
    mDQT: {FFDB}
      BEGIN
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        WHILE (seglength>0) DO
          BEGIN
            stream^.read(DQT.Pq_Tq,1);
{DBG(DQT.Pq_Tq);}
            dec(seglength);
            FOR i:=0 TO 63 DO
              BEGIN
                DQT.Qk[i]:=0;
                CASE (hi4(DQT.Pq_Tq)) OF
                  0:BEGIN
                      stream^.read(DQT.Qk[i],1);
                      dec(seglength,1);
                    END;
                  1:BEGIN
                      stream^.read(DQT.Qk[i],2);
                      dec(seglength,2);
                    END;
                END;
                Qk[lo4(DQT.Pq_Tq)][i]:=DQT.Qk[i];
              END;
          END;
        stream^.seek(stream^.getpos+seglength);
      END;
    mDRI: {FFDD}
      BEGIN
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        restart_interval:=0;
        stream^.read(restart_interval,2);
        swap16(restart_interval);
        dec(seglength,2);
        stream^.seek(stream^.getpos+seglength);
      END;
    mAPP0..mAPP15: {FFE0..FFEF}
      BEGIN
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        stream^.seek(stream^.getpos+seglength);
      END;
    mCOM: {FFFE}
      BEGIN
        stream^.read(seglength,2);
        swap16(seglength);
        dec(seglength,2);
        stream^.seek(stream^.getpos+seglength);
      END;
    END;
  END;

  FUNCTION getcompidx(comp:longint):longint;
  VAR n:longint;
  BEGIN
    n:=0;
    REPEAT
      inc(n);
    UNTIL (n>SOF.Nf) OR (SOF.Ci[n]=comp);
    IF (n>SOF.Nf) THEN n:=1;
    getcompidx:=n;
  END;

  PROCEDURE decode_MCU_SOF0;
  VAR i,j,v,h,vi,hi:longint;
  BEGIN
    FOR i:=1 TO SOS.Ns DO
      BEGIN
        j:=getcompidx(SOS.Csj[i]);
        vi:=lo4(SOF.Hi_Vi[j]);
        hi:=hi4(SOF.Hi_Vi[j]);
        FOR v:=0 TO vi-1 DO
          BEGIN
            FOR h:=0 TO hi-1 DO
              BEGIN
                decode_coefficients_SOF0(i,j,v,h);
                IF end_of_scan THEN break;
              END;
            IF end_of_scan THEN break;
          END;
        IF end_of_scan THEN break;
      END;
    draw;
  END;

  PROCEDURE decode_MCU_SOF2;
  VAR i,j,v,h,vi,hi:longint;
  BEGIN
    FOR i:=1 TO SOS.Ns DO
      BEGIN
        j:=getcompidx(SOS.Csj[i]);
        vi:=lo4(SOF.Hi_Vi[j]);
        hi:=hi4(SOF.Hi_Vi[j]);
        IF (SOS.Ss=0) THEN
          BEGIN
            FOR v:=0 TO vi-1 DO
              BEGIN
                FOR h:=0 TO hi-1 DO
                  BEGIN
                    coeffbufidx[j]:=(coeffY[j]+v)*coeffbufW[j]+coeffX[j]+h;
                    decode_coefficients_SOF2(i,j);
                    IF end_of_scan THEN break;
                  END;
                IF end_of_scan THEN break;
              END;
{drawcoeffbuf;}
            inc(coeffX[j],hi);
            IF (coeffX[j]>=coeffbufXd[j]) THEN
              BEGIN
                coeffX[j]:=0;
                inc(coeffY[j],vi);
                IF (coeffY[j]>=coeffbufYd[j]) THEN end_of_scan:=TRUE;
              END;
            IF end_of_scan THEN break;
          END
        ELSE
          BEGIN
            coeffbufidx[j]:=coeffY[j]*coeffbufW[j]+coeffX[j];
            decode_coefficients_SOF2(i,j);
            inc(coeffX[j]);
            IF (coeffX[j]>=coeffbufXd[j]) THEN
              BEGIN
                coeffX[j]:=0;
                inc(coeffY[j]);
                IF (coeffY[j]>=coeffbufYd[j]) THEN end_of_scan:=TRUE;
              END;
          END;
      END;
  END;

  PROCEDURE decode_restart_interval;
  VAR marker:word;
      c:longint;
  BEGIN
    cnt_nb:=0;
    FOR c:=1 TO maxcomp DO pred[c]:=0;
    end_of_scan:=FALSE;
    REPEAT
      IF (restart_interval=0) THEN
        BEGIN
          CASE SOFtype OF
            mSOF0:decode_MCU_SOF0;
            mSOF1:decode_MCU_SOF0;
            mSOF2:decode_MCU_SOF2;
          END;
        END
      ELSE
        BEGIN
          FOR c:=1 TO restart_interval DO
            BEGIN
        {      decode_MCU; }
              CASE SOFtype OF
                mSOF0:decode_MCU_SOF0;
                mSOF1:decode_MCU_SOF0;
                mSOF2:decode_MCU_SOF2;
              END;
              IF end_of_scan THEN break;
            END;
          end_of_scan:=TRUE;
        END;
    UNTIL end_of_scan;
{drawcoeffbuf;
putimage(0,0,img);
readkey;}
    marker:=interpret_markers(mDNL);
    IF (marker<>mDNL) THEN stream^.seek(stream^.getpos-2);
  END;

  PROCEDURE decode_scan;
  VAR marker:word;
      c:longint;
  BEGIN
    end_of_scan:=FALSE;
    FOR c:=1 TO maxcomp DO
      BEGIN
        coeffbufidx[c]:=0;
        coeffeobrun[c]:=0;
        coeffX[c]:=0;
        coeffY[c]:=0;
      END;
    REPEAT
      decode_restart_interval;
{DBG('***eos');}
{      savepos:=stream^.getpos; }
      marker:=interpret_markers($FFFF);
      IF (marker>=mRST0) AND (marker<=mRST7) THEN
        BEGIN
          end_of_scan:=FALSE;
{DBG('restart'); }
        END
      ELSE
        BEGIN
          stream^.seek(stream^.getpos-2);
        END;
    UNTIL end_of_scan OR end_of_image;
{    drawcoeffbuf; }
  END;

  PROCEDURE decode_frame;
  VAR marker:word;
      c,h,v,v1,h1:longint;
  BEGIN
    IF (SOFtype=mSOF2) THEN
      BEGIN
        v1:=lo4(SOF.Hi_Vi[1]);
        h1:=hi4(SOF.Hi_Vi[1]);
        FOR c:=1 TO SOF.Nf DO
          BEGIN
            v:=lo4(SOF.Hi_Vi[c]);
            h:=hi4(SOF.Hi_Vi[c]);
            coeffbufW[c]:=((SOF.X+15) AND $FFFF) DIV 8;
            coeffbufH[c]:=((SOF.Y+15) AND $FFFF) DIV 8;
            coeffbufXd[c]:=(SOF.X+(h1 DIV h)*8-1) DIV (8*(h1 DIV h));
            coeffbufYd[c]:=(SOF.Y+(v1 DIV v)*8-1) DIV (8*(v1 DIV v));
{DBG(coeffbufW[c]);
DBG(coeffbufH[c]);
DBG(SOF.X);
DBGw(SOF.Y);}
            getmem(coeffbuf[c],coeffbufW[c]*coeffbufH[c]*sizeof(Tcoeffbuf));
            fillchar(coeffbuf[c]^,coeffbufW[c]*coeffbufH[c]*sizeof(Tcoeffbuf),0);
{DBG(coeffbufW[c]*coeffbufH[c]*sizeof(Tcoeffbuf));}
          END;
      END;
    REPEAT
      REPEAT
        marker:=interpret_markers(0);
      UNTIL (marker=mSOS) OR end_of_image;
      decode_scan;
      marker:=interpret_markers(mEOI);
      IF (marker<>mEOI) THEN stream^.seek(stream^.getpos-2);
    UNTIL (marker=mEOI) OR end_of_image;
    IF (SOFtype=mSOF2) THEN
      BEGIN
        drawcoeffbuf;
        FOR c:=1 TO SOF.Nf DO
          BEGIN
            freemem(coeffbuf[c],coeffbufW[c]*coeffbufH[c]*sizeof(Tcoeffbuf));
          END;
      END;
{DBG('%EXIT decode_frame');}
  END;

  PROCEDURE decode_image;
  VAR marker:word;
      u,v,x,y:longint;
  BEGIN
    new(hufftable);
    FOR v:=0 TO 7 DO
      FOR u:=0 TO 7 DO
        costab[v,u]:=trunc(cos((2*v+1)*u*pi/16)*cxx[u]*0.5*256{fixptmul});
    end_of_image:=FALSE;
    restart_interval:=0;
    SOFtype:=0;

    stream^.read(marker,2);
    swap16(marker);
    IF (marker=mSOI) THEN
      BEGIN
        REPEAT
          marker:=interpret_markers(0);
        UNTIL (SOFtype<>0) OR end_of_image;
        IF (SOFtype<>0) THEN decode_frame;
    {    IF (SOFtype=mSOF2) THEN decode_frame; }
      END;
{DBG('%EXIT decode_image');}
    dispose(hufftable);
  END;

{===============================================================}

BEGIN
  LoadImageJPG:=-1;
  IF (nr<>1) THEN exit;
  decode_image;
END;


Function Load_JPG(s:string;var w:virtualwindow):byte;
var l:longint;
    h:PGrpStream;
    b:byte;
begin
w.VWoffset:=0;
h:=New(PGrpStream,Init(s,stOpenRead));
l:=LoadImageJPG(h,w,1);
if l=1 then
   begin
   if w.VWoffset<>0 then Kill_VW(w);
   w.VWoffset:=0;
   end;
Dispose(h,Done);
Load_JPG:=l
end;


Procedure Register_JPG_loader;
begin
RegisterImageLoader('JPG',@Load_JPG);
end;

begin
Register_JPG_loader;
end.
