//------------------------------------------------------------------------------
//
//  Portal Adventure - 2nd PGD Challenge: The Journey
//  Copyright (C) 2012 by Jim Valavanis
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
//  02111-1307, USA.
//
//------------------------------------------------------------------------------
//  E-Mail: jimmyvalavanis@yahoo.gr - jvalavanis@gmail.com
//------------------------------------------------------------------------------

{$I portal.inc}

unit jpg_dAPImin;

{$N+}  { Nomssi: cinfo^.output_gamma }

{ This file contains application interface code for the decompression half
  of the JPEG library.  These are the "minimum" API routines that may be
  needed in either the normal full-decompression case or the
  transcoding-only case.

  Most of the routines intended to be called directly by an application
  are in this file or in jdapistd.c.  But also see jcomapi.c for routines
  shared by compression and decompression, and jdtrans.c for the transcoding
  case. }

{ Original: jdapimin.c ;  Copyright (C) 1994-1998, Thomas G. Lane. }

interface

{$I jconfig.inc}

uses
  jpg_morecfg,
  jpg_error,
  jpg_lib,
  jpg_memmgr,
  jpg_dmarker,
  jpg_dinput,
  jpg_comapi;

{ Nomssi }
procedure jpeg_create_decompress(cinfo: j_decompress_ptr);

{ Initialization of a JPEG decompression object.
  The error manager must already be set up (in case memory manager fails). }

{GLOBAL}
procedure jpeg_CreateDecompress (cinfo: j_decompress_ptr;
                                 version: int;
                                 structsize: size_t);

{ Destruction of a JPEG decompression object }

{GLOBAL}
procedure jpeg_destroy_decompress (cinfo: j_decompress_ptr);


{ Decompression startup: read start of JPEG datastream to see what's there.
  Need only initialize JPEG object and supply a data source before calling.

  This routine will read as far as the first SOS marker (ie, actual start of
  compressed data), and will save all tables and parameters in the JPEG
  object.  It will also initialize the decompression parameters to default
  values, and finally return JPEG_HEADER_OK.  On return, the application may
  adjust the decompression parameters and then call jpeg_start_decompress.
  (Or, if the application only wanted to determine the image parameters,
  the data need not be decompressed.  In that case, call jpeg_abort or
  jpeg_destroy to release any temporary space.)
  If an abbreviated (tables only) datastream is presented, the routine will
  return JPEG_HEADER_TABLES_ONLY upon reaching EOI.  The application may then
  re-use the JPEG object to read the abbreviated image datastream(s).
  It is unnecessary (but OK) to call jpeg_abort in this case.
  The JPEG_SUSPENDED return code only occurs if the data source module
  requests suspension of the decompressor.  In this case the application
  should load more source data and then re-call jpeg_read_header to resume
  processing.
  If a non-suspending data source is used and require_image is TRUE, then the
  return code need not be inspected since only JPEG_HEADER_OK is possible.

  This routine is now just a front end to jpeg_consume_input, with some
  extra error checking. }

{GLOBAL}
function jpeg_read_header (cinfo: j_decompress_ptr;
                           require_image: boolean): int;

{ Consume data in advance of what the decompressor requires.
  This can be called at any time once the decompressor object has
  been created and a data source has been set up.

  This routine is essentially a state machine that handles a couple
  of critical state-transition actions, namely initial setup and
  transition from header scanning to ready-for-start_decompress.
  All the actual input is done via the input controller's consume_input
  method. }

{GLOBAL}
function jpeg_consume_input (cinfo: j_decompress_ptr): int;

{ Have we finished reading the input file? }

{GLOBAL}
function jpeg_input_complete (cinfo: j_decompress_ptr): boolean;

{ Is there more than one scan? }

{GLOBAL}
function jpeg_has_multiple_scans (cinfo: j_decompress_ptr): boolean;


{ Finish JPEG decompression.

  This will normally just verify the file trailer and release temp storage.

  Returns FALSE if suspended.  The return value need be inspected only if
  a suspending data source is used. }

{GLOBAL}
function jpeg_finish_decompress (cinfo: j_decompress_ptr): boolean;

implementation

uses
  d_delphi,
  jpg_deferr;
  
procedure jpeg_create_decompress(cinfo: j_decompress_ptr);
begin
  jpeg_CreateDecompress(cinfo, JPEG_LIB_VERSION,
    size_t(sizeof(jpeg_decompress_struct)));
end;

{ Initialization of a JPEG decompression object.
  The error manager must already be set up (in case memory manager fails). }

{GLOBAL}
procedure jpeg_CreateDecompress (cinfo: j_decompress_ptr;
                                 version: int;
                                 structsize: size_t);
var
  i: int;
var
  err: jpeg_error_mgr_ptr;
  client_data: voidp;
begin
  { Guard against version mismatches between library and caller. }
  cinfo^.mem := nil;    { so jpeg_destroy knows mem mgr not called }
  if (version <> JPEG_LIB_VERSION) then
    ERREXIT2(j_common_ptr(cinfo), JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
  if (structsize <> SizeOf(jpeg_decompress_struct)) then
    ERREXIT2(j_common_ptr(cinfo), JERR_BAD_STRUCT_SIZE,
       int(SizeOf(jpeg_decompress_struct)), int(structsize));

  { For debugging purposes, we zero the whole master structure.
    But the application has already set the err pointer, and may have set
    client_data, so we have to save and restore those fields.
    Note: if application hasn't set client_data, tools like Purify may
    complain here. }
  begin
    err := cinfo^.err;
    client_data := cinfo^.client_data; { ignore Purify complaint here }
    ZeroMemory(j_common_ptr(cinfo), SizeOf(jpeg_decompress_struct));
    cinfo^.err := err;
    cinfo^.client_data := client_data;
  end;
  cinfo^.is_decompressor := TRUE;

  { Initialize a memory manager instance for this object }
  jinit_memory_mgr(j_common_ptr(cinfo));

  { Zero out pointers to permanent structures. }
  cinfo^.progress := nil;
  cinfo^.src := nil;

  for i := 0 to pred(NUM_QUANT_TBLS) do
    cinfo^.quant_tbl_ptrs[i] := nil;

  for i := 0 to pred(NUM_HUFF_TBLS) do
  begin
    cinfo^.dc_huff_tbl_ptrs[i] := nil;
    cinfo^.ac_huff_tbl_ptrs[i] := nil;
  end;

  { Initialize marker processor so application can override methods
    for COM, APPn markers before calling jpeg_read_header.  }
  cinfo^.marker_list := nil;
  jinit_marker_reader(cinfo);

  { And initialize the overall input controller. }
  jinit_input_controller(cinfo);

  { OK, I'm ready }
  cinfo^.global_state := DSTATE_START;
end;


{ Destruction of a JPEG decompression object }

{GLOBAL}
procedure jpeg_destroy_decompress (cinfo: j_decompress_ptr);
begin
  jpeg_destroy(j_common_ptr(cinfo)); { use common routine }
end;


{ Abort processing of a JPEG decompression operation,
  but don't destroy the object itself. }

{GLOBAL}
procedure jpeg_abort_decompress (cinfo: j_decompress_ptr);
begin
  jpeg_abort(j_common_ptr(cinfo)); { use common routine }
end;


{ Set default decompression parameters. }

{LOCAL}
procedure default_decompress_parms (cinfo: j_decompress_ptr);
var
  cid0: int;
  cid1: int;
  cid2: int;
begin
  { Guess the input colorspace, and set output colorspace accordingly. }
  { (Wish JPEG committee had provided a real way to specify this...) }
  { Note application may override our guesses. }
  case (cinfo^.num_components) of
  1: begin
       cinfo^.jpeg_color_space := JCS_GRAYSCALE;
       cinfo^.out_color_space := JCS_GRAYSCALE;
     end;

  3: begin
       if (cinfo^.saw_JFIF_marker) then
       begin
         cinfo^.jpeg_color_space := JCS_YCbCr; { JFIF implies YCbCr }
       end
       else
         if (cinfo^.saw_Adobe_marker) then
         begin
           case (cinfo^.Adobe_transform) of
           0: cinfo^.jpeg_color_space := JCS_RGB;
           1: cinfo^.jpeg_color_space := JCS_YCbCr;
           else
             begin
         WARNMS1(j_common_ptr(cinfo), JWRN_ADOBE_XFORM, cinfo^.Adobe_transform);
               cinfo^.jpeg_color_space := JCS_YCbCr; { assume it's YCbCr }
             end;
           end;
         end
         else
         begin
           { Saw no special markers, try to guess from the component IDs }
           cid0 := cinfo^.comp_info^[0].component_id;
           cid1 := cinfo^.comp_info^[1].component_id;
           cid2 := cinfo^.comp_info^[2].component_id;

           if (cid0 = 1) and (cid1 = 2) and (cid2 = 3) then
       cinfo^.jpeg_color_space := JCS_YCbCr { assume JFIF w/out marker }
           else
             if (cid0 = 82) and (cid1 = 71) and (cid2 = 66) then
               cinfo^.jpeg_color_space := JCS_RGB { ASCII 'R', 'G', 'B' }
             else
             begin
               {$IFDEF DEBUG}
         TRACEMS3(j_common_ptr(cinfo), 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2);
               {$ENDIF}
               cinfo^.jpeg_color_space := JCS_YCbCr; { assume it's YCbCr }
             end;
         end;
       { Always guess RGB is proper output colorspace. }
       cinfo^.out_color_space := JCS_RGB;
     end;

  4: begin
       if (cinfo^.saw_Adobe_marker) then
       begin
         case (cinfo^.Adobe_transform) of
         0: cinfo^.jpeg_color_space := JCS_CMYK;
         2: cinfo^.jpeg_color_space := JCS_YCCK;
         else
           begin
             WARNMS1(j_common_ptr(cinfo), JWRN_ADOBE_XFORM, cinfo^.Adobe_transform);
             cinfo^.jpeg_color_space := JCS_YCCK; { assume it's YCCK }
           end;
         end;
       end
       else
       begin
         { No special markers, assume straight CMYK. }
         cinfo^.jpeg_color_space := JCS_CMYK;
       end;
       cinfo^.out_color_space := JCS_CMYK;
     end;

  else
    begin
      cinfo^.jpeg_color_space := JCS_UNKNOWN;
      cinfo^.out_color_space := JCS_UNKNOWN;
    end;
  end;

  { Set defaults for other decompression parameters. }
  cinfo^.scale_num := 1;    { 1:1 scaling }
  cinfo^.scale_denom := 1;
  cinfo^.output_gamma := 1.0;
  cinfo^.buffered_image := FALSE;
  cinfo^.raw_data_out := FALSE;
  cinfo^.dct_method := JDCT_DEFAULT;
  cinfo^.do_fancy_upsampling := TRUE;
  cinfo^.do_block_smoothing := TRUE;
  cinfo^.quantize_colors := FALSE;
  { We set these in case application only sets quantize_colors. }
  cinfo^.dither_mode := JDITHER_FS;
{$ifdef QUANT_2PASS_SUPPORTED}
  cinfo^.two_pass_quantize := TRUE;
{$else}
  cinfo^.two_pass_quantize := FALSE;
{$endif}
  cinfo^.desired_number_of_colors := 256;
  cinfo^.colormap := nil;
  { Initialize for no mode change in buffered-image mode. }
  cinfo^.enable_1pass_quant := FALSE;
  cinfo^.enable_external_quant := FALSE;
  cinfo^.enable_2pass_quant := FALSE;
end;


{ Decompression startup: read start of JPEG datastream to see what's there.
  Need only initialize JPEG object and supply a data source before calling.

  This routine will read as far as the first SOS marker (ie, actual start of
  compressed data), and will save all tables and parameters in the JPEG
  object.  It will also initialize the decompression parameters to default
  values, and finally return JPEG_HEADER_OK.  On return, the application may
  adjust the decompression parameters and then call jpeg_start_decompress.
  (Or, if the application only wanted to determine the image parameters,
  the data need not be decompressed.  In that case, call jpeg_abort or
  jpeg_destroy to release any temporary space.)
  If an abbreviated (tables only) datastream is presented, the routine will
  return JPEG_HEADER_TABLES_ONLY upon reaching EOI.  The application may then
  re-use the JPEG object to read the abbreviated image datastream(s).
  It is unnecessary (but OK) to call jpeg_abort in this case.
  The JPEG_SUSPENDED return code only occurs if the data source module
  requests suspension of the decompressor.  In this case the application
  should load more source data and then re-call jpeg_read_header to resume
  processing.
  If a non-suspending data source is used and require_image is TRUE, then the
  return code need not be inspected since only JPEG_HEADER_OK is possible.

  This routine is now just a front end to jpeg_consume_input, with some
  extra error checking. }

{GLOBAL}
function jpeg_read_header (cinfo: j_decompress_ptr;
                           require_image: boolean): int;
var
  retcode: int;
begin
  if (cinfo^.global_state <> DSTATE_START) and
     (cinfo^.global_state <> DSTATE_INHEADER) then
    ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state);

  retcode := jpeg_consume_input(cinfo);

  case (retcode) of
  JPEG_REACHED_SOS:
    retcode := JPEG_HEADER_OK;
  JPEG_REACHED_EOI:
    begin
      if (require_image) then   { Complain if application wanted an image }
        ERREXIT(j_common_ptr(cinfo), JERR_NO_IMAGE);
      { Reset to start state; it would be safer to require the application to
        call jpeg_abort, but we can't change it now for compatibility reasons.
        A side effect is to free any temporary memory (there shouldn't be any). }

      jpeg_abort(j_common_ptr(cinfo)); { sets state := DSTATE_START }
      retcode := JPEG_HEADER_TABLES_ONLY;
    end;
  JPEG_SUSPENDED: ;    { no work }
  end;

  jpeg_read_header := retcode;
end;


{ Consume data in advance of what the decompressor requires.
  This can be called at any time once the decompressor object has
  been created and a data source has been set up.

  This routine is essentially a state machine that handles a couple
  of critical state-transition actions, namely initial setup and
  transition from header scanning to ready-for-start_decompress.
  All the actual input is done via the input controller's consume_input
  method. }

{GLOBAL}
function jpeg_consume_input (cinfo: j_decompress_ptr): int;
var
  retcode: int;
begin
  retcode := JPEG_SUSPENDED;

  { NB: every possible DSTATE value should be listed in this switch }

  if (cinfo^.global_state) = DSTATE_START then
  begin {work around the FALLTHROUGH}
    { Start-of-datastream actions: reset appropriate modules }
    cinfo^.inputctl^.reset_input_controller (cinfo);
    { Initialize application's data source module }
    cinfo^.src^.init_source (cinfo);
    cinfo^.global_state := DSTATE_INHEADER;
  end;

  case (cinfo^.global_state) of
  DSTATE_START,
  DSTATE_INHEADER:
    begin
      retcode := cinfo^.inputctl^.consume_input (cinfo);
      if (retcode = JPEG_REACHED_SOS) then
      begin { Found SOS, prepare to decompress }
        { Set up default parameters based on header data }
        default_decompress_parms(cinfo);
        { Set global state: ready for start_decompress }
        cinfo^.global_state := DSTATE_READY;
      end;
    end;
  DSTATE_READY:
    { Can't advance past first SOS until start_decompress is called }
    retcode := JPEG_REACHED_SOS;

  DSTATE_PRELOAD,
  DSTATE_PRESCAN,
  DSTATE_SCANNING,
  DSTATE_RAW_OK,
  DSTATE_BUFIMAGE,
  DSTATE_BUFPOST,
  DSTATE_STOPPING:
    retcode := cinfo^.inputctl^.consume_input (cinfo);
  else
    ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state);
  end;
  jpeg_consume_input := retcode;
end;


{ Have we finished reading the input file? }

{GLOBAL}
function jpeg_input_complete (cinfo: j_decompress_ptr): boolean;
begin
  { Check for valid jpeg object }
  if (cinfo^.global_state < DSTATE_START) or
     (cinfo^.global_state > DSTATE_STOPPING) then
    ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state);
  jpeg_input_complete := cinfo^.inputctl^.eoi_reached;
end;


{ Is there more than one scan? }

{GLOBAL}
function jpeg_has_multiple_scans (cinfo: j_decompress_ptr): boolean;
begin
  { Only valid after jpeg_read_header completes }
  if (cinfo^.global_state < DSTATE_READY) or
     (cinfo^.global_state > DSTATE_STOPPING) then
    ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state);
  jpeg_has_multiple_scans := cinfo^.inputctl^.has_multiple_scans;
end;


{ Finish JPEG decompression.

  This will normally just verify the file trailer and release temp storage.

  Returns FALSE if suspended.  The return value need be inspected only if
  a suspending data source is used. }

{GLOBAL}
function jpeg_finish_decompress (cinfo: j_decompress_ptr): boolean;
begin
  if ((cinfo^.global_state = DSTATE_SCANNING) or
      (cinfo^.global_state = DSTATE_RAW_OK) and (not cinfo^.buffered_image)) then
  begin
    { Terminate final pass of non-buffered mode }
    if (cinfo^.output_scanline < cinfo^.output_height) then
      ERREXIT(j_common_ptr(cinfo), JERR_TOO_LITTLE_DATA);
    cinfo^.master^.finish_output_pass (cinfo);
    cinfo^.global_state := DSTATE_STOPPING;
  end
  else
    if (cinfo^.global_state = DSTATE_BUFIMAGE) then
    begin
      { Finishing after a buffered-image operation }
      cinfo^.global_state := DSTATE_STOPPING;
    end
    else
      if (cinfo^.global_state <> DSTATE_STOPPING) then
      begin
        { STOPPING := repeat call after a suspension, anything else is error }
        ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state);
      end;
  { Read until EOI }
  while (not cinfo^.inputctl^.eoi_reached) do
  begin
    if (cinfo^.inputctl^.consume_input (cinfo) = JPEG_SUSPENDED) then
    begin
      jpeg_finish_decompress := FALSE;  { Suspend, come back later }
      exit;
    end;
  end;
  { Do final cleanup }
  cinfo^.src^.term_source (cinfo);
  { We can use jpeg_abort to release memory and reset global_state }
  jpeg_abort(j_common_ptr(cinfo));
  jpeg_finish_decompress := TRUE;
end;

end.