Resources.pas
上传用户:yj_qiu
上传日期:2022-08-08
资源大小:23636k
文件大小:55k
源码类别:

游戏引擎

开发平台:

Delphi

  1. (*
  2.  @Abstract(Basic resources unit)
  3.  (C) 2006 George "Mirage" Bakhtadze. <a href="http://www.casteng.com">www.casteng.com</a> <br>
  4.  The source code may be used under either MPL 1.1 or LGPL 2.1 license. See included license.txt file <br>
  5.  Unit contains basic resource classes
  6. *)
  7. {$Include GDefines.inc}
  8. unit Resources;
  9. interface
  10. uses SysUtils,
  11.      TextFile,
  12.      BaseTypes, Basics, BaseMsg, Base2D, Base3D, Props, BaseClasses;
  13. const
  14.   // Image mip levels policy enumeration string
  15.   MipPolicyEnum = 'No mips' + StringDelimiter + 'Persistent' + StringDelimiter + 'Generated';
  16.   // Image filters enumeration string
  17.   ImageFilterEnums = 'None'    + StringDelimiter + 'Simple 2X' + StringDelimiter +
  18.                      'Box'     + StringDelimiter + 'Triangle'  + StringDelimiter +
  19.                      'Hermite' + StringDelimiter + 'Bell'      + StringDelimiter +
  20.                      'Spline'  + StringDelimiter + 'Lanczos'   + StringDelimiter +
  21.                      'Mitchell';
  22.   // Minimum mega image block size
  23.   MinBlockSize = 32;
  24. type
  25.   TResString = type AnsiString;
  26.   // Mip (LOD) levels policy
  27.   TMipPolicy = (// No mip levels used
  28.                 mpNoMips,
  29.                 // Mip levels are persistent and stored with original image
  30.                 mpPersistent,
  31.                 // Mip levels are generated and not stored with original image
  32.                 mpGenerated);
  33.   // Base resource class)
  34.   TResource = class(TItem)
  35.   private
  36.     FLoaded: Boolean;
  37.     FFormat: Cardinal;
  38.     FData: Pointer;
  39.     FDataSize, DataOffsetInStream: Integer;
  40.     function GetData: Pointer;
  41.     function GetTotalElements: Integer;
  42.   protected
  43.     // Returns number of bytes which should be allocated by the resource in a storage stream
  44.     function GetDataSizeInStream: Integer; virtual;
  45.     // Should perform actual conversion from old format to a new one and return True if the conversion is possible and successful
  46.     function Convert(OldFormat, NewFormat: Cardinal): Boolean; virtual;
  47.     // Calls Convert() and if it returns True sets the new format
  48.     procedure SetFormat(const Value: Cardinal);
  49.     // Read resource's data from the specified stream
  50.     function LoadData(Stream: Basics.TStream): Boolean; virtual;
  51.     // Not used yet
  52.     procedure SetLoaded(Value: Boolean); virtual;
  53.     // Not used yet
  54.     procedure UnloadData; virtual;
  55.   public
  56.     constructor Create(AManager: TItemsManager); override;
  57.     destructor Destroy; override;
  58.     class function IsAbstract: Boolean; override;
  59.     function GetItemSize(CountChilds: Boolean): Integer; override;
  60.     procedure AddProperties(const Result: Props.TProperties); override;
  61.     procedure SetProperties(Properties: Props.TProperties); override;
  62.     // Allocates an empty data buffer or changes allocated size of an existing one
  63.     procedure Allocate(ASize: Integer);
  64.     // Sets already allocated and probably ready to use data
  65.     procedure SetAllocated(ASize: Integer; AData: Pointer);
  66.     // Returns size of each element in resource
  67.     function GetElementSize: Integer; virtual;
  68.     // Loads the resource from a stream
  69.     function Load(Stream: Basics.TStream): Boolean; override;
  70.     // Saves the resource to a stream
  71.     function Save(Stream: Basics.TStream): Boolean; override;
  72.     // Resource format
  73.     property Format: Cardinal read FFormat write SetFormat;
  74.     // Determines if the resource is loaded completely including its data
  75.     property Loaded: Boolean read FLoaded write SetLoaded;
  76.     // Data size in bytes
  77.     property DataSize: Integer read FDataSize write Allocate;
  78.     // Data size in bytes in stream
  79.     property DataSizeInStream: Integer read GetDataSizeInStream;
  80.     // Pointer to the resource's data
  81.     property Data: Pointer read GetData;
  82.     // Number of elements in the resource
  83.     property TotalElements: Integer read GetTotalElements;
  84.   end;
  85.   // This message should be sent to core handler and possibly broadcasted if data of a resource has been modified
  86.   TResourceModifyMsg = class(TNotificationMessage)
  87.     // Resource, containing the modified data
  88.     Resource: TResource;
  89.     // AResource - a resource, containing the modified data
  90.     constructor Create(AResource: TResource);
  91.   end;
  92.   // Base class for all array-based resources
  93.   TArrayResource = class(TResource)
  94.     procedure AddProperties(const Result: Props.TProperties); override;
  95.     procedure SetProperties(Properties: Props.TProperties); override;
  96.   end;
  97.   // @Abstract(Stores an image)
  98.   TImageResource = class(TResource)
  99.   private
  100.     procedure SetMipPolicy(const Value: TMipPolicy); virtual;
  101.     procedure ObtainFilter(OldWidth, OldHeight, NewWidth, NewHeight: Integer; out OFilter: TImageResizeFilter; out OFilterValue: Single);
  102.     procedure SetMinFilter(const Value: TImageResizeFilter);
  103.     procedure SetMagFilter(const Value: TImageResizeFilter);
  104.     function GetActualLevels: Integer;
  105.   protected
  106.     // Image width
  107.     FWidth,
  108.     // Image height
  109.     FHeight: Integer;
  110.     // Information about mip levels
  111.     FLevels: TImageLevels;
  112.     // Number of mip levels requested (via properties). 0 to use FSuggestedLevels.
  113.     FRequestedLevels,
  114.     // Suggested number of mip levels based on dimensions
  115.     FSuggestedLevels,
  116.     // Number of bits per pixel
  117.     FBitsPerPixel: Integer;
  118.     // Mip levels policy
  119.     FMipPolicy: TMipPolicy;
  120.     // Filter used when the image size is decreased and for mipmaps calculation
  121.     FMinFilter,
  122.     // Filter used when the image size is increased. Image width have more priority than height when choosing filter.
  123.     FMagFilter: TImageResizeFilter;
  124.     // Parameter value for minification filter
  125.     FMinFilterParameter,
  126.     // Parameter value for magnification filter
  127.     FMagFilterParameter: Single;
  128.     // Images with generated mipmaps needs less space in storage stream
  129.     function GetDataSizeInStream: Integer; override;
  130.     // Returns information about specified mip level
  131.     function GetLevelInfo(Index: Integer): TImageLevel;
  132.     // Performs image conversion from one format to another
  133.     function Convert(OldFormat, NewFormat: Cardinal): Boolean; override;
  134.   public
  135.     // Resource containing image's palette (for paletted image formats only).
  136.     PaletteResource: TArrayResource;
  137.     constructor Create(AManager: TItemsManager); override;
  138.     procedure AddProperties(const Result: Props.TProperties); override;
  139.     procedure SetProperties(Properties: Props.TProperties); override;
  140.     function GetElementSize: Integer; override;
  141.     function Save(Stream: Basics.TStream): Boolean; override;
  142.     function Load(Stream: Basics.TStream): Boolean; override;
  143.     // Creates an empty image with the specified dimensions
  144.     procedure CreateEmpty(AWidth, AHeight: Integer); virtual;
  145.     // Sets width and height of the image. Data should be initialized. deprecated: @Link(MinFilter)/@Link(MagFilter) will be used to resize.
  146.     procedure SetDimensions(AWidth, AHeight: Integer); virtual;
  147.     // Generates mip data
  148.     procedure GenerateMipLevels(ARect: BaseTypes.TRect);
  149.     // Image width
  150.     property Width: Integer read FWidth;
  151.     // Image height
  152.     property Height: Integer read FHeight;
  153.     // Mip levels policy
  154.     property MipPolicy: TMipPolicy read FMipPolicy write SetMipPolicy;
  155.     // Suggested mip levels
  156.     property SuggestedLevels: Integer read FSuggestedLevels;
  157.     // Actual number of mip levels
  158.     property ActualLevels: Integer read GetActualLevels;
  159.     // Mip levels information
  160.     property LevelInfo[Index: Integer]: TImageLevel read GetLevelInfo;
  161.     // Filter used when the image size is decreased and for mipmaps calculation
  162.     property MinFilter: TImageResizeFilter read FMinFilter write SetMinFilter;
  163.     // Filter used when the image size is increased. Image width have more priority than height when choosing filter.
  164.     property MagFilter: TImageResizeFilter read FMagFilter write SetMagFilter;
  165.     // Parameter value for minification filter
  166.     property MinFilterParameter: Single read FMinFilterParameter write FMinFilterParameter;
  167.     // Parameter value for magnification filter
  168.     property MagFilterParameter: Single read FMagFilterParameter write FMagFilterParameter;
  169.   end;
  170.   // @Abstract(Stores a texture)
  171.   TTextureResource = class(TImageResource)
  172.   private
  173.     FMipLevels: Integer;
  174.     procedure SetMipLevels(const Value: Integer);
  175.   public
  176.     procedure AddProperties(const Result: Props.TProperties); override;
  177.     procedure SetProperties(Properties: Props.TProperties); override;
  178.     function GetMipLevelData(ALevel: Integer): Pointer;
  179.     // Number of mip-levels
  180.     property MipLevels: Integer read FMipLevels write SetMipLevels;
  181.   end;
  182.   // @Abstract(Stores a sound)
  183.   TAudioResource = class(TArrayResource)
  184.   end;
  185.   // @Abstract(Stores some text)
  186.   TTextResource = class(TArrayResource)
  187.   protected
  188.     // Returns text stored by the resource
  189.     function GetText: TResString; virtual;
  190.     // Sets text stored by the resource
  191.     procedure SetText(const NewText: TResString); virtual;
  192.   public
  193.     function GetElementSize: Integer; override;
  194.     // Text stored by the resource
  195.     property Text: TResString read GetText write SetText;
  196.   end;
  197.   { @Abstract(Stores a script)
  198.     @Link(Text) property returns script's source text. @Link(Data) stores compiled version.
  199.     When @Link(Source) property is changed a message of class <b>TDataModifyMsg</b> will be broadcasted to allow timely update of <b>data</b> property. }
  200.   TScriptResource = class(TTextResource)
  201.   protected
  202.     // Script source text
  203.     FSource: TResString;
  204.     // Compiled code size. Zero value means that code size is same as the resource data size.
  205.     FCodeSize: Integer;
  206.     // Returns source text
  207.     function GetText: TResString; override;
  208.     // Sets source text
  209.     procedure SetText(const NewText: TResString); override;
  210.   public
  211.     // Sets compiled code size if it has different value than resource data size (in case if some other information is stored within the resource)
  212.     procedure SetCodeSize(ACodeSize: Integer);
  213.     procedure AddProperties(const Result: Props.TProperties); override;
  214.     procedure SetProperties(Properties: Props.TProperties); override;
  215.     // Source text
  216.     property Source: TResString read FSource write SetText;
  217.     // Compiled code size. Zero value means that code size is same as the resource data size.
  218.     property CodeSize: Integer read FCodeSize;
  219.   end;
  220.   // @Abstract(Stores a path)
  221.   TPathResource = class(TArrayResource)
  222.   end;
  223.   // @Abstract(Stores an UV-corrdinates mapping)
  224.   TUVMapResource = class(TArrayResource)
  225.     function GetElementSize: Integer; override;
  226.   end;
  227.   // @Abstract(Stores a characted mapping)
  228.   TCharMapResource = class(TArrayResource)
  229.     function GetElementSize: Integer; override;
  230.   end;
  231.   // @Abstract(Stores a palette)
  232.   TPaletteResource = class(TArrayResource)
  233.     function GetElementSize: Integer; override;
  234.   end;
  235.   // Data structure used for mega image caching
  236.   TCahceRec = record
  237.     Level, X, Y: Integer;
  238.     Data: Pointer;
  239.   end;
  240.   { Stores an extra large image which can not be handled as usual due to its size. The image is stored in a stream
  241.     divided into blocks. Some number of blocks are cached in memory.
  242.     Optimal block size and cache size depending on how the mega image will be used and should be determined empirically. }
  243.   TMegaImageResource = class(TImageResource)
  244.   private
  245.     FBlockWidth, FBlockHeight, ActualBlockWidth, ActualBlockHeight, FNumBlocksX, FNumBlocksY: Integer;
  246.     FDataStream: TStream;
  247.     FStoreFileName, FSourceFileName: TFileName;
  248.     FCacheTotal, FCacheCurrent: Integer;
  249.     FCacheData: array of array of array of Pointer;      // MipLevels * FNumBlocksX * FNumBlocksY
  250.     FCacheStart, FCacheEnd: Integer;
  251.     FCache: array of TCahceRec;
  252.     procedure SetMipPolicy(const Value: TMipPolicy); override;
  253.     // Stores in FDataStream the image divided in blocks of the specified size and retuns True if success
  254.     function Prepare(AImageSource: TStream): Boolean;
  255.     procedure DelCacheBlock;
  256.     function AddCacheBlock(ALevel, AX, AY: Integer): Pointer;
  257.     // Inits cache
  258.     procedure InitCache(ACacheTotal: Integer);
  259.     // Inits internal parameters. Returns True if all parameters are correct.
  260.     function Init(ABlockWidth, ABlockHeight: Integer): Boolean;
  261.     // Writes the specified cached data block to data stream and returns True if success.
  262.     function SaveBlockData(ALevel, ABlockX, ABlockY: Integer): Boolean;
  263.   public
  264.     destructor Destroy; override;
  265.     procedure AddProperties(const Result: Props.TProperties); override;
  266.     procedure SetProperties(Properties: Props.TProperties); override;
  267.     procedure CreateEmpty(AWidth, AHeight: Integer); override;
  268.     procedure SetDimensions(AWidth, AHeight: Integer); override;
  269.     // Returns address of data of the specified block. Puts the block into cache if it was not here already.
  270.     function GetBlockData(ALevel, ABlockX, ABlockY: Integer): Pointer;
  271.     { Copies a sequence of ALength pixels starting at (AX, AY) from the specified mip level of the megaimage
  272.       to an image with width DestImageWidth and data located in memory at Dest and returns True if success }
  273.     function LoadSeq(AX, AY, ALength, ALevel: Integer; Dest: Pointer): Boolean;
  274.     // Copies a rectangular area of the specified mip level of the megaimage to an image with width DestImageWidth and data located in memory at Dest and returns True if success
  275.     function LoadRect(const Rect: TRect; ALevel: Integer; Dest: Pointer; DestImageWidth: Integer): Boolean;
  276.     // Copies a rectangular area of the specified mip level of the megaimage to an RGBA image with width DestImageWidth and data located in memory at Dest and returns True if success
  277.     function LoadRectAsRGBA(Rect: TRect; ALevel: Integer; Dest: Pointer; DestImageWidth: Integer): Boolean;
  278.     { Copies a sequence of ALength pixels from an image with data located in memory at Src to
  279.       a rectangular area on the specified mip level of the mega image starting at (AX, AY) from the specified mip level of the megaimage and returns True if success }
  280.     function SaveSeq(AX, AY, ALength, ALevel: Integer; Src: Pointer): Boolean;
  281.     { Copies a rectangular area from an image with width SrcImageWidth and data located in memory at Src
  282.       to the specified mip level of the megaimage and returns True if success. Rebuilds all mipmaps lower than Level if BuildMips is True. }
  283.     function SaveRect(Rect: TRect; ALevel: Integer; Src: Pointer; SrcImageWidth: Integer; BuildMips: Boolean): Boolean;
  284.     // Data store stream
  285.     property DataStream: TStream read FDataStream write FDataStream;
  286.   end;
  287.   // Image source impementation for mega images
  288.   TMegaImageSource = class(TBaseImageSource)
  289.   private
  290.     FResource: TMegaImageResource;
  291.     FLevel: Integer;
  292.   protected  
  293.     function GetData(const Rect: TRect; Dest: Pointer; DestImageWidth: Integer): Boolean; override;
  294.     function GetDataAsRGBA(const Rect: TRect; Dest: Pointer; DestImageWidth: Integer): Boolean; override;
  295.   public
  296.     constructor Create(AResource: TMegaImageResource; ALevel: Integer);
  297.   end;
  298.   TMegaImagePaintOp = class(TImageOperation)
  299.   private
  300.     FTempData: Pointer;
  301.     FResource: TMegaImageResource;
  302.     FLevel: Integer;
  303.   protected
  304.     procedure DoApply; override;
  305.   public
  306.     constructor Create(X, Y: Integer; AResource: TMegaImageResource; ALevel: Integer; ABrush: TBrush; const ARect: BaseTypes.TRect);
  307.     destructor Destroy; override;
  308.   end;
  309.   // Returns list of classes introduced by the unit
  310.   function GetUnitClassList: TClassArray;
  311. implementation
  312. function GetUnitClassList: TClassArray;
  313. begin
  314.   Result := GetClassList([TResource, TImageResource, TMegaImageResource, TArrayResource, TCharMapResource, TUVMapResource, TAudioResource, TTextResource, TScriptResource]);
  315. end;
  316. { TResource }
  317. function TResource.GetDataSizeInStream: Integer;
  318. begin
  319.   Result := FDataSize;
  320. end;
  321. function TResource.Convert(OldFormat, NewFormat: Cardinal): Boolean;
  322. begin
  323.   Result := not Assigned(Data);
  324. //  Result := True;
  325. end;
  326. procedure TResource.SetFormat(const Value: Cardinal);
  327. var Changed: Boolean; OldData: Pointer;
  328. begin
  329.   OldData := Data;
  330.   {$IFDEF DEBUGMODE} FConsistent := False; {$ENDIF}      // The resource is not valid within the Convert() method because its contents is not compliant to Format variable
  331.   Changed := (Value <> FFormat) and Convert(FFormat, Value);
  332.   if Changed then FFormat := Value;
  333.   {$IFDEF DEBUGMODE} FConsistent := True; {$ENDIF}
  334.   if Assigned(FManager) then begin
  335.     if (FData <> OldData) then SendMessage(TDataAdressChangeMsg.Create(OldData, FData, True), nil, [mfCore, mfBroadcast]);
  336.     if Changed then SendMessage(TResourceModifyMsg.Create(Self), nil, [mfCore]);
  337.   end;
  338. end;
  339. function TResource.GetData: Pointer;
  340. begin
  341. //  Result := nil;
  342. //  if not Loaded then if LoadData(Root.Core.DataStream) <> feOK then Exit;
  343.   Result := FData;
  344. end;
  345. function TResource.LoadData(Stream: Basics.TStream): Boolean;
  346. begin
  347.   Result := False;
  348. {.$IFDEF COMPATMODE}
  349.   if (DataOffsetInStream = -1) or (Stream = nil) then begin
  350.     {$IFDEF LOGGING} Log.Log('TResource.LoadData: Failed to load resource data', lkError); {$ENDIF}
  351.     Exit;
  352.   end;
  353.   if not Stream.Seek(DataOffsetInStream) then Exit;
  354. {.$ENDIF}
  355.   GetMem(FData, DataSize);
  356.   if not Stream.ReadCheck(FData^, DataSize) then Exit;
  357.   Result  := True;
  358.   FLoaded := True;
  359. end;
  360. constructor TResource.Create(AManager: TItemsManager);
  361. begin
  362.   inherited;
  363.   FLoaded            := True;
  364.   FData              := nil;
  365.   FDataSize          :=  0;
  366.   DataOffsetInStream := -1;
  367. end;
  368. destructor TResource.Destroy;
  369. begin
  370.   UnloadData;
  371.   inherited;
  372. end;
  373. class function TResource.IsAbstract: Boolean;
  374. begin
  375.   Result := Self = TResource;
  376. end;
  377. function TResource.GetItemSize(CountChilds: Boolean): Integer;
  378. begin
  379.   Result := inherited GetItemSize(CountChilds);
  380.   if FLoaded then Inc(Result, FDataSize);
  381. end;
  382. procedure TResource.AddProperties(const Result: Props.TProperties);
  383. begin
  384.   inherited;
  385.   if not Assigned(Result) then Exit;
  386.   Result.Add('Format'   , vtNat,    [poReadonly],           IntToStrA(Format),    '');
  387.   Result.Add('Data size', vtInt,    [poReadonly, poHidden], IntToStrA(FDataSize), '');
  388.   Result.AddBinary('Data', [poReadonly, poHidden], FData, DataSizeInStream);
  389. end;
  390. procedure TResource.SetProperties(Properties: Props.TProperties);
  391. var Prop: PProperty; RealDataSize: Integer;
  392. begin
  393.   inherited;
  394.   if Properties.Valid('Format')    then Format := StrToInt64Def(Properties['Format'], 0);
  395.   if Properties.Valid('Data size') then begin
  396.     if Properties.Valid('Data') then begin
  397.       Prop := Properties.GetProperty('Data');
  398.       RealDataSize := StrToIntDef(Prop.Enumeration, FDataSize);
  399.       if RealDataSize = FDataSize then
  400.         SetAllocated(StrToIntDef(Properties['Data size'], FDataSize), Pointer(StrToIntDef(Properties['Data'], Integer(FData))))
  401.       else begin
  402.         SetAllocated(RealDataSize, Pointer(StrToIntDef(Properties['Data'], Integer(FData))));
  403.         Allocate(StrToIntDef(Properties['Data size'], FDataSize));
  404.       end;
  405.     end else Allocate(StrToIntDef(Properties['Data size'], FDataSize));
  406.   end;
  407. end;
  408. procedure TResource.Allocate(ASize: Integer);
  409. var OldData: Pointer;
  410. begin
  411.   if (ASize = FDataSize) and (FData <> nil) then Exit;
  412.   OldData := FData;
  413.   ReallocMem(FData, ASize);
  414.   FDataSize := ASize;
  415.   FLoaded := True;
  416.   if Assigned(FManager) and (FData <> OldData) then SendMessage(TDataAdressChangeMsg.Create(OldData, FData, OldData <> nil), nil, [mfCore, mfBroadcast]);
  417. end;
  418. procedure TResource.SetAllocated(ASize: Integer; AData: Pointer);
  419. var OldData: Pointer;
  420. begin
  421.   Assert((ASize = 0) or Assigned(AData));
  422.   OldData := FData;
  423.   FDataSize := ASize;
  424.   if (FData <> AData) and (FData <> nil) then FreeMem(FData);
  425.   FData     := AData;
  426.   FLoaded   := True;
  427.   if Assigned(FManager) and (FData <> OldData)
  428.      {$IFDEF DEBUGMODE} and FConsistent {$ENDIF} then SendMessage(TDataAdressChangeMsg.Create(OldData, FData, True), nil, [mfCore, mfBroadcast]);
  429. end;
  430. function TResource.GetElementSize: Integer;
  431. begin
  432.   Result := DataSize;
  433. end;
  434. function TResource.GetTotalElements: Integer;
  435. begin
  436.   Assert(FDataSize mod GetElementSize = 0, ClassName + '.GetTotalElements: Invalid data size');
  437.   Result := FDataSize div GetElementSize;
  438. end;
  439. function TResource.Load(Stream: Basics.TStream): Boolean;
  440. begin
  441. //  {$IFNDEF COMPATMODE}
  442. //  Result := feCannotRead;
  443. //  if LoadData(Stream) <> feOK then Exit;
  444. //  UnloadData;
  445.   Result := inherited Load(Stream);
  446. //  {$ELSE}
  447. {  if not inherited Load(Stream) then Exit;
  448.   DataOffsetInStream := Stream.Position;
  449.   if not Stream.Seek(Stream.Position + Cardinal(DataSize)) then Exit;       // Move to the next
  450.   UnloadData;
  451.   LoadData(Stream);
  452.   Result := True;}
  453. //  {$ENDIF}
  454. end;
  455. function TResource.Save(Stream: Basics.TStream): Boolean;
  456. begin
  457. //  Result := feCannotRead;
  458. //  if not Loaded and (LoadData(Stream) <> feOK) then Exit;         // Try to load data if it's not loaded
  459. //  Result := feCannotWrite;
  460. //  if Stream.Write(FData^, DataSize) <> feOK then Exit;
  461.   Result := inherited Save(Stream);
  462. end;
  463. procedure TResource.UnloadData;
  464. begin
  465.   if FData <> nil then FreeMem(FData);
  466.   FData := nil;
  467.   FLoaded := False;
  468. end;
  469. procedure TResource.SetLoaded(Value: Boolean);
  470. begin
  471.   if FLoaded = Value then Exit;
  472. end;
  473. { TResourceModifyMsg }
  474. constructor TResourceModifyMsg.Create(AResource: TResource);
  475. begin
  476.   Resource := AResource;
  477. end;
  478. { TTextResource }
  479. function TTextResource.GetElementSize: Integer;
  480. const s: TResString = 'A';
  481. begin
  482.   Result := SizeOf(s[1]);
  483. end;
  484. function TTextResource.GetText: TResString;
  485. begin
  486.   SetLength(Result, TotalElements);
  487.   if FData <> nil then Move(FData^, Result[1], FDataSize);
  488. end;
  489. procedure TTextResource.SetText(const NewText: TResString);
  490. begin
  491.   Allocate(Length(NewText) * GetElementSize);
  492.   if FData <> nil then Move(NewText[1], FData^, FDataSize);
  493. end;
  494. { TArrayResource }
  495. procedure TArrayResource.AddProperties(const Result: Props.TProperties);
  496. begin
  497.   inherited;
  498.   if not Assigned(Result) then Exit;
  499.   Result.Add('Total elements', vtInt, [poReadonly], IntToStrA(TotalElements), '');
  500. end;
  501. procedure TArrayResource.SetProperties(Properties: Props.TProperties);
  502. begin
  503.   inherited;
  504. //  if Properties.Valid('Total elements') then TotalElements := Properties.GetAsInteger('Total elements');
  505. end;
  506. { TImageResource }
  507. procedure TImageResource.SetMipPolicy(const Value: TMipPolicy);
  508. var NewSize, OldSize: Integer; NewData: Pointer; NeedGenerateMips: Boolean;
  509. begin
  510.   if (Value = FMipPolicy) then Exit;
  511.   Assert(ActualLevels > 0);
  512.   NeedGenerateMips := (FMipPolicy <> mpGenerated) and (Value = mpGenerated);
  513.   OldSize := FLevels[ActualLevels-1].Offset + FLevels[ActualLevels-1].Size;// Width * Height * GetBytesPerPixel(FFormat);
  514.   FMipPolicy := Value; // May change the value of ActualLevels
  515.   if (FFormat = pfUndefined) or (GetBytesPerPixel(FFormat) = 0) or
  516.      (FWidth = 0) or (FHeight = 0) or
  517.      (DataSize = 0) or not Assigned(FData) then Exit;
  518.   NewSize := FLevels[ActualLevels-1].Offset + FLevels[ActualLevels-1].Size;
  519. //  if Value =  mpNoMips then NewSize := OldSize;
  520. //  if (Value <> mpNoMips) and  then
  521. //    NewSize := FLevels[SuggestedLevels-1].Offset + FLevels[SuggestedLevels-1].Size;
  522.   if (NewSize <> DataSize) and
  523.      (FFormat <> pfUndefined) and (GetBytesPerPixel(FFormat) <> 0) and
  524.      (FWidth <> 0) and (FHeight <> 0) and
  525.      (DataSize <> 0) and Assigned(FData) then begin
  526.     {$IFDEF DEBUGMODE} Log.Log('TImageResource.SetMipPolicy: Reallocating image "' + Name + '"'); {$ENDIF}
  527.     GetMem(NewData, NewSize);
  528.     if NewData = nil then begin
  529.       Log.Log('TImageResource.SetMipPolicy: Not enough memory', lkError);
  530.       Exit;
  531.     end;
  532.     Move(FData^, NewData^, MinI(OldSize, NewSize));
  533.     SetAllocated(NewSize, NewData);
  534.   end;
  535.   if NeedGenerateMips then GenerateMipLevels(GetRect(0, 0, Width, Height));
  536.   SendMessage(TResourceModifyMsg.Create(Self), nil, [mfCore]);
  537. end;
  538. procedure TImageResource.ObtainFilter(OldWidth, OldHeight, NewWidth, NewHeight: Integer; out OFilter: TImageResizeFilter; out OFilterValue: Single);
  539. begin
  540.   if 2*(NewWidth - OldWidth) + (NewHeight - OldHeight) > 0 then begin
  541.     OFilter      := MagFilter;
  542.     OFilterValue := MagFilterParameter;
  543.   end else begin
  544.     OFilter      := MinFilter;
  545.     OFilterValue := MinFilterParameter;
  546.   end;
  547. end;
  548. procedure TImageResource.SetMinFilter(const Value: TImageResizeFilter);
  549. begin
  550.   FMinFilter := Value;
  551.   if DefaultResizeFilterValue[FMinFilter] <> 0 then FMinFilterParameter := DefaultResizeFilterValue[FMinFilter];
  552. end;
  553. procedure TImageResource.SetMagFilter(const Value: TImageResizeFilter);
  554. begin
  555.   FMagFilter := Value;
  556.   if DefaultResizeFilterValue[FMagFilter] <> 0 then FMagFilterParameter := DefaultResizeFilterValue[FMagFilter];
  557. end;
  558. function TImageResource.GetActualLevels: Integer;
  559. begin
  560.   if MipPolicy = mpNoMips then
  561.     Result := 1
  562.   else if FRequestedLevels = 0 then
  563.     Result := FSuggestedLevels
  564.   else
  565.     Result := FRequestedLevels;
  566. end;
  567. function TImageResource.Convert(OldFormat, NewFormat: Cardinal): Boolean;
  568. var
  569.   NewData: Pointer;
  570.   NewSize: Integer;
  571.   PaletteData: Pointer;
  572.   PaletteElements: Integer;
  573. begin
  574.   Result := True;
  575.   Assert(OldFormat <> NewFormat);
  576.   if OldFormat = NewFormat then Exit;
  577.   FBitsPerPixel := GetBitsPerPixel(NewFormat);
  578.   if GetBytesPerPixel(NewFormat) = 0 then begin
  579.     Log.Log(SysUtils.Format('%S(%S).%S: Invalid image format: %D', [ClassName, GetFullName, 'SetFormat', NewFormat]), lkError);
  580.     Result := False;
  581.     Exit;
  582.   end;
  583.   if (OldFormat <> pfUndefined) then begin
  584.     NewSize := TotalElements * GetBytesPerPixel(NewFormat);
  585.     GetMem(NewData, NewSize);
  586.   end else
  587.     NewSize := 0;
  588.   if Assigned(PaletteResource) then begin
  589.     PaletteElements := PaletteResource.TotalElements;
  590.     PaletteData     := PaletteResource.FData;
  591.   end else begin
  592.     PaletteElements := 0; PaletteData := nil;
  593.   end;
  594.   if (OldFormat <> pfUndefined) and (FWidth <> 0) and (FHeight <> 0) then
  595.     if ConvertImage(OldFormat, NewFormat, TotalElements, FData, PaletteElements, PaletteData, NewData) then begin
  596.       FFormat := NewFormat;                            // To make the resource valid in SetAllocated
  597.       {$IFDEF DEBUGMODE} FConsistent := True; {$ENDIF}
  598.       SetAllocated(NewSize, NewData);
  599.       FSuggestedLevels := GetSuggestedMipLevelsInfo(FWidth, FHeight, FFormat, FLevels);
  600.       {$IFDEF DEBUGMODE} Log.Log(SysUtils.Format('%S("%S").%S: Image format changed', [ClassName, GetFullName, 'SetFormat']), lkWarning); {$ENDIF}
  601.     end else begin
  602.       {$IFDEF LOGGING} Log.Log(SysUtils.Format('%S(%S).%S: Unsupported format conversion: %D to %D', [ClassName, GetFullName, 'SetFormat', OldFormat, NewFormat]), lkError); {$ENDIF}
  603.     end;
  604. end;
  605. function TImageResource.GetDataSizeInStream: Integer;
  606. begin
  607.   Result := FDataSize;
  608.   if MipPolicy = mpGenerated then Result := Width * Height * GetBytesPerPixel(Format);
  609. end;
  610. function TImageResource.GetLevelInfo(Index: Integer): TImageLevel;
  611. begin
  612.   Result := FLevels[Index];
  613. end;
  614. constructor TImageResource.Create(AManager: TItemsManager);
  615. begin
  616.   inherited;
  617.   MipPolicy     := mpNoMips;
  618.   MinFilter     := ifLanczos;
  619.   MagFilter     := ifLanczos;
  620. end;
  621. procedure TImageResource.AddProperties(const Result: Props.TProperties);
  622. begin
  623.   inherited;
  624.   if not Assigned(Result) then Exit;
  625.   Result.Add('Width',  vtInt, [], IntToStrA(FWidth),  '');
  626.   Result.Add('Height', vtInt, [], IntToStrA(FHeight), '');
  627.   Result.AddEnumerated('FormatImage', [], Format, PixelFormatsEnum);
  628.   Result.AddEnumerated('Mip Policy', [], Ord(FMipPolicy), MipPolicyEnum);
  629.   Result.Add('Mip levels',     vtInt, [], IntToStr(FRequestedLevels), '');
  630.   Result.Add('Current levels', vtInt, [poReadOnly], IntToStr(ActualLevels), '');
  631.   Result.AddEnumerated('Min filter', [], Ord(MinFilter), ImageFilterEnums);
  632.   Result.AddEnumerated('Mag filter', [], Ord(MagFilter), ImageFilterEnums);
  633.   Result.Add('Min filter value', vtSingle, [], FloatToStr(FMinFilterParameter), '');
  634.   Result.Add('Mag filter value', vtSingle, [], FloatToStr(FMagFilterParameter), '');  
  635.   Result.Add('Mip recalc', vtBoolean, [], OnOffStr[False], '');
  636.   if not Assigned(Data) then Result.Add('Create empty', vtBoolean, [], OnOffStr[False], '');
  637. end;
  638. procedure TImageResource.SetProperties(Properties: Props.TProperties);
  639. var NewWidth, NewHeight: Integer;
  640. begin
  641.   inherited;
  642.   if Properties.Valid('FormatImage') then Format := Properties.GetAsInteger('FormatImage');
  643.   if Properties.Valid('Width')  then NewWidth  := Properties.GetAsInteger('Width')  else NewWidth  := FWidth;
  644.   if Properties.Valid('Height') then NewHeight := Properties.GetAsInteger('Height') else NewHeight := FHeight;
  645.   if Properties.Valid('Min filter') then MinFilter := TImageResizeFilter(Properties.GetAsInteger('Min filter'));
  646.   if Properties.Valid('Mag filter') then MagFilter := TImageResizeFilter(Properties.GetAsInteger('Mag filter'));
  647.   if Properties.Valid('Min filter value') then FMinFilterParameter := StrToFloatDef(Properties['Min filter value'], 0);
  648.   if Properties.Valid('Mag filter value') then FMagFilterParameter := StrToFloatDef(Properties['Mag filter value'], 0);
  649.   if Properties.Valid('Mip levels') then begin
  650.     FRequestedLevels := StrToIntDef(Properties['Mip levels'], 0);
  651.     SendMessage(TResourceModifyMsg.Create(Self), nil, [mfCore]);
  652.   end;
  653.   if Properties.Valid('Mip Policy') then MipPolicy := TMipPolicy(Properties.GetAsInteger('Mip Policy'));
  654.   if (NewWidth <> FWidth) or (NewHeight <> FHeight) then SetDimensions(NewWidth, NewHeight);
  655.   if Properties.Valid('Create empty') and (Properties.GetAsInteger('Create empty') > 0) then
  656.     CreateEmpty(NewWidth, NewHeight);
  657.   if Properties.Valid('Mip recalc') and (Properties.GetAsInteger('Mip recalc') > 0) or
  658.      Properties.Valid('Mip Policy') and (TMipPolicy(Properties.GetAsInteger('Mip Policy')) = mpGenerated) then
  659.     GenerateMipLevels(GetRect(0, 0, Width, Height));  
  660. end;
  661. function TImageResource.GetElementSize: Integer;
  662. begin
  663.   Result := GetBytesPerPixel(Format);
  664.   if Result = 0 then Result := DataSize;
  665. end;
  666. function TImageResource.Save(Stream: Basics.TStream): Boolean;
  667. begin
  668.   Result := inherited Save(Stream);
  669. end;
  670. function TImageResource.Load(Stream: Basics.TStream): Boolean;
  671. begin
  672.   Result := inherited Load(Stream);
  673. end;
  674. procedure TImageResource.CreateEmpty(AWidth, AHeight: Integer);
  675. begin
  676.   if (AWidth <> 0) and (AHeight <> 0) then begin
  677.     FSuggestedLevels := GetSuggestedMipLevelsInfo(AWidth, AHeight, FFormat, FLevels);
  678.     if (FFormat <> pfUndefined) and (GetBytesPerPixel(FFormat) <> 0) then
  679.       Allocate(FLevels[ActualLevels-1].Offset + FLevels[ActualLevels-1].Size);
  680.   end;
  681.   FWidth  := AWidth;
  682.   FHeight := AHeight;
  683.   FSuggestedLevels := GetSuggestedMipLevelsInfo(FWidth, FHeight, FFormat, FLevels);
  684.   SendMessage(TResourceModifyMsg.Create(Self), nil, [mfCore]);
  685. end;
  686. procedure TImageResource.SetDimensions(AWidth, AHeight: Integer);
  687. var NewData: Pointer; NewSize: Integer;
  688. begin
  689.   if (FWidth = AWidth) and (FHeight = AHeight) then Exit;
  690.   if (AWidth <> 0) and (AHeight <> 0) then begin
  691.     FSuggestedLevels := GetSuggestedMipLevelsInfo(AWidth, AHeight, FFormat, FLevels);
  692.     NewSize := FLevels[ActualLevels-1].Offset + FLevels[ActualLevels-1].Size;
  693.     if Assigned(Data) and (FDataSize <> NewSize) and (FFormat <> pfUndefined) and (GetBytesPerPixel(FFormat) <> 0) then begin
  694.       GetMem(NewData, NewSize);
  695.       Move(Data^, NewData^, MinI(DataSize, NewSize));
  696. //      ResizeImage(GetRect(0, 0, Width, Height), GetRect(0, 0, AWidth, AHeight), AWidth, NewData);
  697.       SetAllocated(NewSize, NewData);
  698.       {$IFDEF DEBUGMODE} Log.Log(SysUtils.Format('%S("%S").%S: Image dimensions changed', [ClassName, Name, 'SetDimensions']), lkWarning); {$ENDIF}
  699.     end;
  700.   end;
  701.   FWidth  := AWidth;
  702.   FHeight := AHeight;
  703.   FSuggestedLevels := GetSuggestedMipLevelsInfo(FWidth, FHeight, FFormat, FLevels);
  704.   SendMessage(TResourceModifyMsg.Create(Self), nil, [mfCore]);
  705. end;
  706. procedure TImageResource.GenerateMipLevels(ARect: BaseTypes.TRect);
  707.   procedure CorrectRect(var LRect: BaseTypes.TRect; Level: Integer);
  708.   begin
  709.     LRect.Left   := LRect.Left - Ord(Odd(LRect.Left));
  710.     LRect.Top    := LRect.Top  - Ord(Odd(LRect.Top));
  711.     LRect.Right  := MinI(LevelInfo[Level].Width,  LRect.Right  + Ord(Odd(LRect.Right)));
  712.     LRect.Bottom := MinI(LevelInfo[Level].Height, LRect.Bottom + Ord(Odd(LRect.Bottom)));
  713.   end;
  714. var k, w, h: Integer; ORect, LRect, LastRect: BaseTypes.TRect; Filter: TImageResizeFilter; FilterValue: Single;
  715. begin
  716.   if not Assigned(Data) or (FMipPolicy = mpNoMips) then Exit;
  717.   ARect.Left   := ClampI(ARect.Left,   0, Width);
  718.   ARect.Top    := ClampI(ARect.Top,    0, Height);
  719.   ARect.Right  := ClampI(ARect.Right,  0, Width);
  720.   ARect.Bottom := ClampI(ARect.Bottom, 0, Height);
  721.   ORect := ARect;
  722.   CorrectRect(ORect, 0);
  723.   LRect := ARect;
  724.   for k := 0 to ActualLevels-2 do begin
  725.     CorrectRect(LRect, k);
  726.     LastRect := LRect;
  727.     LRect.Left   := LRect.Left   div 2;
  728.     LRect.Top    := LRect.Top    div 2;
  729.     LRect.Right  := LRect.Right  div 2;
  730.     LRect.Bottom := LRect.Bottom div 2;
  731.     w := LRect.Right  - LRect.Left;
  732.     h := LRect.Bottom - LRect.Top;
  733.     if (w = 0) and (h = 0) then Break;
  734.     w := MaxI(1, w);
  735.     h := MaxI(1, h);
  736.     ObtainFilter(Width, Height, w, h, Filter, FilterValue);
  737.     Base2D.ResizeImage(Filter, FilterValue, Format, PtrOffs(Data, LevelInfo[k].Offset),   LastRect, LevelInfo[k].Width,
  738.                                                     PtrOffs(Data, LevelInfo[k+1].Offset), LRect,    LevelInfo[k+1].Width);
  739.   end;
  740.   {$IFDEF DEBUGMODE} Log.Log('TImageResource.GenerateMipLevels: Image "' + Name + '"'); {$ENDIF}
  741.   SendMessage(TResourceModifyMsg.Create(Self), nil, [mfCore]);
  742. end;
  743. { TTextureResource }
  744. procedure TTextureResource.SetMipLevels(const Value: Integer);
  745. begin
  746.   FMipLevels := Value;
  747. end;
  748. procedure TTextureResource.AddProperties(const Result: Props.TProperties);
  749. begin
  750.   inherited;
  751.   if not Assigned(Result) then Exit;
  752. //  Result.Add('Auto generate mips', vtBoolean, [], OnOffStr[AutoGenerateMips], '');
  753.   Result.Add('Mip levels',         vtInt,     [], IntToStr(Miplevels),        '');
  754. end;
  755. procedure TTextureResource.SetProperties(Properties: Props.TProperties);
  756. begin
  757.   inherited;
  758. //  if Properties.Valid('Auto generate mips') then AutoGenerateMips := Properties.GetAsInteger('Auto generate mips') > 0;
  759.   if Properties.Valid('Mip levels') then Miplevels := Properties.GetAsInteger('Mip levels');
  760. end;
  761. function TTextureResource.GetMipLevelData(ALevel: Integer): Pointer;
  762. begin
  763.   Result := Pointer(Integer(Data) + FLevels[ALevel].Offset);
  764. end;
  765. { TUVMapResource }
  766. function TUVMapResource.GetElementSize: Integer;
  767. begin
  768.   Result := SizeOf(TUV);
  769. end;
  770. { TCharMapResource }
  771. function TCharMapResource.GetElementSize: Integer;
  772. begin
  773.   Result := SizeOf(TCharMapItem);
  774. end;
  775. { TPaletteResource }
  776. function TPaletteResource.GetElementSize: Integer;
  777. begin
  778.   Result := SizeOf(TPaletteItem);
  779. end;
  780. { TScriptResource }
  781. function TScriptResource.GetText: TResString;
  782. begin
  783.   Result := FSource;
  784. end;
  785. procedure TScriptResource.SetText(const NewText: TResString);
  786. begin
  787.   FSource := NewText;
  788.   SendMessage(TDataModifyMsg.Create(Self), nil, [mfCore, mfBroadcast]);
  789. //  if FSource <> '' then
  790.   SetAllocated(0, nil);                       // Invalidate existing compiled script
  791. end;
  792. procedure TScriptResource.SetCodeSize(ACodeSize: Integer);
  793. begin
  794.   FCodeSize := ACodeSize;
  795. end;
  796. procedure TScriptResource.AddProperties(const Result: TProperties);
  797. begin
  798.   inherited;
  799.   if Assigned(Result) then begin
  800.     Result.Add('Source', vtString, [], Source, '');
  801.     Result.Add('Code size', vtInt, [poReadonly], IntToStr(FCodeSize), '');
  802.   end;
  803. end;
  804. procedure TScriptResource.SetProperties(Properties: TProperties);
  805. begin
  806.   if Properties.Valid('Source') then Source := Properties['Source'];          // Source should be assigned prior to data to preserve the data while loading
  807.   if Properties.Valid('Code size') then FCodeSize := StrToIntDef(Properties['Code size'], 0);
  808.   inherited;
  809. end;
  810. { TMegaImageResource }
  811. procedure TMegaImageResource.SetMipPolicy(const Value: TMipPolicy);
  812. begin
  813.   inherited SetMipPolicy(mpPersistent);
  814. end;
  815. function TMegaImageResource.Prepare(AImageSource: TStream): Boolean;
  816. const ReadPhaseW = 0.1; CMipPhaseW = 0.3; WritePhaseW = 0.6;
  817. var
  818.   i, j, k, m,
  819.   SrcBpP, BpP,
  820.   lw, lh, bw, bh: Integer;
  821.   Buffer, Temp, CTemp: Pointer;
  822.   Garbage: IRefcountedContainer;
  823.   Header: TImageHeader;
  824. begin
  825.   Result := False;
  826.   if not Assigned(FDataStream) or not Assigned(AImageSource) then Exit;
  827.   if not LoadBitmapHeader(AImageSource, Header) then begin
  828.     Header.Format       := Format;
  829.     Header.Width        := Width;
  830.     Header.Height       := Height;
  831.     Header.BitsPerPixel := GetBitsPerPixel(Header.Format);
  832.     Header.LineSize     := Header.Width * Header.BitsPerPixel div 8;
  833.     Header.ImageSize    := Header.LineSize * Header.Height;
  834.     Header.PaletteSize  := 0;
  835.     Header.Palette      := nil;
  836.   end;
  837.   SrcBpP := GetBytesPerPixel(Header.Format);
  838.   BpP    := GetBytesPerPixel(Format);
  839.   if (Header.Width = 0) or (Header.Height = 0) or (SrcBpP = 0) then begin
  840.     Log.Log('TMegaImageResource.Prepare: Invalid source stream format', lkError);
  841.     Exit;
  842.   end;
  843.   InitCache(FCacheTotal);                        // To clear cache
  844.   SetDimensions(Header.Width, Header.Height);
  845.   if not Init(FBlockWidth, FBlockHeight) then Exit;
  846.   FDataStream.Size := LevelInfo[SuggestedLevels-1].Offset + LevelInfo[SuggestedLevels-1].Size;
  847.   if AImageSource.Size - AImageSource.Position < Cardinal(Width * Height * GetBytesPerPixel(Header.Format)) then begin
  848.     Log.Log('TMegaImageResource.Prepare: Not enough data in stream', lkError);
  849.     Exit;
  850.   end;
  851.   Garbage := CreateRefcountedContainer;
  852.   GetMem(Buffer, FLevels[SuggestedLevels-1].Offset + FLevels[SuggestedLevels-1].Size);
  853.   SetAllocated(Width * Height * BpP, Buffer);
  854.   Garbage.AddPointer(Buffer);
  855.   GetMem(Temp, Width * FBlockHeight * SrcBpP);
  856.   Garbage.AddPointer(Temp);
  857.   GetMem(CTemp, FBlockWidth * FBlockHeight * BpP);
  858.   Garbage.AddPointer(CTemp);
  859.   FDataStream.Seek(0);
  860.   for k := 0 to FNumBlocksY - 1 do begin
  861.     SendMessage(TProgressMsg.Create(ReadPhaseW * k / (FNumBlocksY - 1)), nil, [mfCore]);
  862.     if not AImageSource.ReadCheck(Temp^, Width * FBlockHeight * SrcBpP) then begin
  863.       Log.Log('TMegaImageResource.Prepare: Error reading from stream', lkError);
  864.       Exit;
  865.     end;
  866.     for i := 0 to FBlockHeight-1 do
  867.       if not ConvertImage(Header.Format, Format, Width, PtrOffs(Temp, i*Width*SrcBpP), 0, nil,
  868.                                                         PtrOffs(Buffer, ((FNumBlocksY-k) * FBlockHeight - i - 1) * Width * BpP)) then begin
  869.         Log.Log('TMegaImageResource.Prepare: Format conversion ' + PixelFormatToStr(Header.Format) + ' to ' + PixelFormatToStr(Format) + ' not supported', lkError);
  870.         Exit;
  871.       end;
  872.   end;
  873.   GenerateMipLevels(GetRect(0, 0, Width, Height));
  874.   SendMessage(TProgressMsg.Create(CMipPhaseW), nil, [mfCore]);
  875.   for m := 0 to ActualLevels-1 do begin
  876.     lw := LevelInfo[m].Width;
  877.     lh := LevelInfo[m].Height;
  878.     bw := MinI(lw, FBlockWidth);
  879.     bh := MinI(lh, FBlockHeight);
  880.     for k := 0 to lh div bh - 1 do begin
  881.       for j := 0 to lw div bw - 1 do begin
  882.         BufferCut(PtrOffs(Buffer, LevelInfo[m].Offset), CTemp, lw, bw, BpP, GetRect(j * bw, k * bh, (j+1) * bw, (k+1) * bh));
  883.         if not FDataStream.WriteCheck(CTemp^, bw*bh*BpP) then Exit;
  884.       end;
  885.     end;
  886.     SendMessage(TProgressMsg.Create(CMipPhaseW + (1-CMipPhaseW) * m / MaxI(1, (ActualLevels-1))), nil, [mfCore]);
  887.   end;
  888. {  for k := 0 to FNumBlocksY - 1 do begin
  889.     SendMessage(TProgressMsg.Create(k / (FNumBlocksY - 1)), nil, [mfCore]);
  890.     if not AImageSource.ReadCheck(Src^, Width * FBlockHeight * SrcBpP) then Exit;
  891.     for i := 0 to FNumBlocksX-1 do for j := 0 to FBlockHeight-1 do begin
  892.       if not ConvertImage(Header.Format, Format, FBlockWidth, PtrOffs(Src, (j*Width + i*FBlockWidth) * SrcBpP), 0, nil,
  893.                                                    PtrOffs(Temp, j*FBlockWidth*BpP)) or
  894.          not FDataStream.WriteCheck(PtrOffs(Temp, j*FBlockWidth*BpP)^, FBlockWidth*BpP) then Exit;
  895.     end;
  896.   end;}
  897.   FData := nil;                                 // To prevent freeing in SetAllocated()
  898.   SetAllocated(0, nil);                      
  899.   Result := True;
  900. end;
  901. procedure TMegaImageResource.DelCacheBlock;
  902. begin
  903.   FCacheData[FCache[FCacheStart].Level, FCache[FCacheStart].Y, FCache[FCacheStart].X] := nil;
  904.   FCacheStart := (FCacheStart + 1) mod FCacheTotal;
  905.   Dec(FCacheCurrent);
  906. end;
  907. function TMegaImageResource.AddCacheBlock(ALevel, AX, AY: Integer): Pointer;
  908. begin
  909.   Assert(FCacheCurrent <= FCacheTotal);
  910.   if FCacheCurrent = FCacheTotal then DelCacheBlock;
  911.   with FCache[(FCacheStart + FCacheCurrent) mod FCacheTotal] do begin
  912.     Level := ALevel;
  913.     X     := AX;
  914.     Y     := AY;
  915.     Result := Data;
  916.   end;
  917.   Inc(FCacheCurrent);
  918. end;
  919. procedure TMegaImageResource.InitCache(ACacheTotal: Integer);
  920.   function CacheEmpty: Boolean;
  921.   var i, j, k: Integer;
  922.   begin
  923.     Result := True;
  924.     if FCacheTotal = 0 then Exit;
  925.     for k := 0 to ActualLevels-1 do
  926.       for j := 0 to FNumBlocksY-1 do for i := 0 to FNumBlocksX-1 do Result := Result and (FCacheData[k, j, i] = nil);
  927.   end;
  928. var i: Integer;
  929. begin
  930.   for i := 0 to FCacheCurrent-1 do DelCacheBlock;
  931.   for i := 0 to FCacheTotal-1 do FreeMem(FCache[i].Data);
  932. //  Assert(CacheEmpty);
  933.   FCacheTotal := ACacheTotal;
  934.   SetLength(FCache, FCacheTotal);
  935.   for i := 0 to FCacheTotal-1 do GetMem(FCache[i].Data, ActualBlockWidth * ActualBlockHeight * FBitsPerPixel div 8);
  936.   FCacheCurrent := 0;
  937.   FCacheStart   := 0;
  938.   FCacheEnd     := -1;
  939.   SetLength(FCacheData, ActualLevels, FNumBlocksY, FNumBlocksX);
  940. end;
  941. function TMegaImageResource.Init(ABlockWidth, ABlockHeight: Integer): Boolean;
  942. begin
  943.   Result := False;
  944.   if (ABlockWidth = 0) or (FBlockHeight = 0) or (Width mod ABlockWidth <> 0) or (Height mod ABlockHeight <> 0) then begin
  945.     Log.Log('TMegaImageResource.Prepare: Width/Height should be nonzero and divide by BlockWidth/BlockHeight', lkError);
  946.     Exit;
  947.   end;
  948.   FBlockWidth  := ABlockWidth;
  949.   FBlockHeight := ABlockHeight;
  950.   FNumBlocksX := Width  div FBlockWidth;
  951.   FNumBlocksY := Height div FBlockHeight;
  952.   ActualBlockWidth  := FBlockWidth;
  953.   ActualBlockHeight := FBlockHeight;
  954.   InitCache(FCacheTotal);
  955.   Result := True;
  956. end;
  957. function TMegaImageResource.SaveBlockData(ALevel, ABlockX, ABlockY: Integer): Boolean;
  958. begin
  959.   Result := False;
  960.   if FCacheData[ALevel, ABlockY, ABlockX] = nil then Exit;
  961.   if not FDataStream.Seek(LevelInfo[ALevel].Offset + ((FNumBlocksX div (1 shl ALevel)) * ABlockY + ABlockX) * ActualBlockWidth * ActualBlockHeight * FBitsPerPixel div 8) then begin
  962.     ErrorHandler(TStreamError.Create('TMegaImageResource.SaveBlockData: Error seeking stream'));
  963.     Exit;
  964.   end else if not FDataStream.WriteCheck(FCacheData[ALevel, ABlockY, ABlockX]^, MinI(LevelInfo[ALevel].Width, ActualBlockWidth) * MinI(LevelInfo[ALevel].Height, ActualBlockHeight) * FBitsPerPixel div 8) then
  965.     ErrorHandler(TStreamError.Create('TMegaImageResource.SaveBlockData: Error writing stream'));
  966.   Result := True;
  967. end;
  968. destructor TMegaImageResource.Destroy;
  969. begin
  970.   FreeAndNil(FDataStream);
  971.   InitCache(0);
  972.   inherited;
  973. end;
  974. procedure TMegaImageResource.AddProperties(const Result: TProperties);
  975. begin
  976.   inherited;
  977.   if Assigned(Result) then begin
  978.     Result.Add('Store file',  vtString,  [], FStoreFileName, '');
  979.     Result.Add('Reinit',              vtBoolean, [], OnOffStr[False], '');
  980.     Result.Add('ReinitSource file',  vtString,  [], FSourceFileName, '');
  981.     Result.Add('ReinitBlock width',  vtInt,     [], IntToStr(FBlockWidth),  '');
  982.     Result.Add('ReinitBlock height', vtInt,     [], IntToStr(FBlockHeight), '');
  983.     Result.Add('CacheNumber of blocks', vtInt, [],           IntToStr(FCacheTotal),   '');
  984.     Result.Add('CacheCurrent blocks',   vtInt, [poReadonly], IntToStr(FCacheCurrent), '');
  985.   end;
  986. end;
  987. procedure TMegaImageResource.CreateEmpty(AWidth, AHeight: Integer);
  988. begin
  989.   // Do nothing
  990. end;
  991. procedure TMegaImageResource.SetDimensions(AWidth, AHeight: Integer);
  992. begin
  993.   FWidth  := AWidth;
  994.   FHeight := AHeight;
  995.   FSuggestedLevels := GetSuggestedMipLevelsInfo(FWidth, FHeight, FFormat, FLevels);
  996.   Init(FBlockWidth, FBlockHeight);
  997. end;
  998. procedure TMegaImageResource.SetProperties(Properties: TProperties);
  999. var Stream: TStream;
  1000. begin
  1001.   inherited;
  1002.   if Properties.Valid('Store file') then begin
  1003.     FStoreFileName := Properties['Store file'];
  1004. //    if not Assigned(DataStream) then
  1005.     FDataStream := TFileStream.Create(FStoreFileName);
  1006.   end;
  1007.   if Properties.Valid('ReinitSource file')  then FSourceFileName := Properties['ReinitSource file'];
  1008.   if Properties.Valid('ReinitBlock width') or Properties.Valid('ReinitBlock height') then begin
  1009.     if Properties.Valid('ReinitBlock width')  then FBlockWidth  := StrToIntDef(Properties['ReinitBlock width'],  0);
  1010.     if Properties.Valid('ReinitBlock height') then FBlockHeight := StrToIntDef(Properties['ReinitBlock height'], 0);
  1011.     Init(FBlockWidth, FBlockHeight);
  1012.   end;
  1013.   if Properties.Valid('CacheNumber of blocks') then InitCache(MaxI(1, StrToIntDef(Properties['CacheNumber of blocks'], 1)));
  1014.   if Properties.Valid('Reinit') and (Properties.GetAsInteger('Reinit') > 0) then begin
  1015.     Stream := TFileStream.Create(FSourceFileName);
  1016.     if not Prepare(Stream) then Log.Log('TMegaImageResource: Reinit failed', lkError);
  1017.     FreeAndNil(Stream);
  1018.   end else Log.Log('TMegaImageResource.SetProperties: Reinit may be needed after properties change');
  1019. end;
  1020. function TMegaImageResource.GetBlockData(ALevel, ABlockX, ABlockY: Integer): Pointer;
  1021. begin
  1022.   Result := nil;
  1023.   if FCacheData[ALevel, ABlockY, ABlockX] = nil then begin
  1024.     FCacheData[ALevel, ABlockY, ABlockX] := AddCacheBlock(ALevel, ABlockX, ABlockY);
  1025.     if (FCacheData[ALevel, ABlockY, ABlockX] = nil) then Exit;
  1026.     if not FDataStream.Seek(LevelInfo[ALevel].Offset + ((FNumBlocksX div (1 shl ALevel)) * ABlockY + ABlockX) * ActualBlockWidth * ActualBlockHeight * FBitsPerPixel div 8) then begin
  1027.       Log.Log('TMegaImageResource.GetBlockData: Error seeking stream', lkError);
  1028.       Exit;
  1029.     end else if not FDataStream.ReadCheck(FCacheData[ALevel, ABlockY, ABlockX]^, MinI(LevelInfo[ALevel].Width, ActualBlockWidth) * MinI(LevelInfo[ALevel].Height, ActualBlockHeight) * FBitsPerPixel div 8) then
  1030.       Log.Log('TMegaImageResource.GetBlockData: Error reading stream', lkError)
  1031.   end;
  1032.   Result := FCacheData[ALevel, ABlockY, ABlockX];
  1033. end;
  1034. function TMegaImageResource.LoadSeq(AX, AY, ALength, ALevel: Integer; Dest: Pointer): Boolean;
  1035. var BlockX, BlockY, BlockXOfs, BlockYOfs, BpP, Len, abw: Integer; Temp: Pointer;
  1036. begin
  1037.   Result := False;
  1038.   if (ActualBlockWidth < MinBlockSize) or (ActualBlockHeight < MinBlockSize) then Exit;
  1039.   BpP := GetBytesPerPixel(Format);
  1040.   BlockX := AX div ActualBlockWidth;
  1041.   BlockY := AY div ActualBlockHeight;
  1042.   BlockXOfs := AX - BlockX * ActualBlockWidth;
  1043.   BlockYOfs := AY - BlockY * ActualBlockHeight;
  1044.   abw := MinI(LevelInfo[ALevel].Width, ActualBlockWidth);
  1045. //  Log.Log(SysUtils.Format('*** (%D, %D), Bl (%D, %D) ', [AX, AY, BlockX, BlockY]));
  1046.   while ALength > 0 do begin
  1047.     Temp := GetBlockData(ALevel, BlockX, BlockY);
  1048.     if Temp = nil then Exit;
  1049.     Len := MinI(abw - BlockXOfs, ALength);
  1050.     Move(PtrOffs(Temp, (BlockYOfs * abw + BlockXOfs)*BpP)^, Dest^, Len*BpP);
  1051.     Dec(ALength, Len);
  1052.     Dest := PtrOffs(Dest, Len * BpP);
  1053.     BlockXOfs := 0;
  1054.     Inc(BlockX);
  1055.   end;
  1056.   Result := True;
  1057. end;
  1058. function TMegaImageResource.LoadRect(const Rect: TRect; ALevel: Integer; Dest: Pointer; DestImageWidth: Integer): Boolean;
  1059. var i, Left, Top, Right, Bottom: Integer;
  1060. begin
  1061.   Result := False;
  1062.   Left   := ClampI(Rect.Left,   0, LevelInfo[ALevel].Width);
  1063.   Right  := ClampI(Rect.Right,  0, LevelInfo[ALevel].Width);
  1064.   Top    := ClampI(Rect.Top,    0, LevelInfo[ALevel].Height);
  1065.   Bottom := ClampI(Rect.Bottom, 0, LevelInfo[ALevel].Height);
  1066.   {$IFDEF DEBUGMODE}
  1067.   for i := Rect.Top to Top-1 do
  1068.     FillChar(PtrOffs(Dest, (i-Rect.Top)*DestImageWidth*FBitsPerPixel div 8)^, 0, (Rect.Right - Rect.Left)*FBitsPerPixel div 8);
  1069.   for i := Bottom to Rect.Bottom-1 do
  1070.     FillChar(PtrOffs(Dest, (i-Rect.Top)*DestImageWidth*FBitsPerPixel div 8)^, 0, (Rect.Right - Rect.Left)*FBitsPerPixel div 8);
  1071.   {$ENDIF}
  1072.   for i := Top to Bottom-1 do begin
  1073.     {$IFDEF DEBUGMODE} FillChar(PtrOffs(Dest, (i-Rect.Top)*DestImageWidth*FBitsPerPixel div 8)^, 0, (Left - Rect.Left)*FBitsPerPixel div 8);{$ENDIF}
  1074.     if not LoadSeq(Left, i, Right-Left, ALevel, PtrOffs(Dest, ((i-Rect.Top)*DestImageWidth+Left - Rect.Left)*FBitsPerPixel div 8)) then Exit;
  1075.     {$IFDEF DEBUGMODE} FillChar(PtrOffs(Dest, ((i-Rect.Top)*DestImageWidth+Right - Rect.Left)*FBitsPerPixel div 8)^, 0, (Rect.Right - Right)*FBitsPerPixel div 8);{$ENDIF}
  1076.   end;
  1077.   Result := True;
  1078. end;
  1079. function TMegaImageResource.LoadRectAsRGBA(Rect: TRect; ALevel: Integer; Dest: Pointer; DestImageWidth: Integer): Boolean;
  1080. const MaxLineLength = $FFFF;
  1081. var i, w: Integer; Temp: array[0..MaxLineLength-1] of TColor;
  1082. begin
  1083.   Result := False;
  1084.   Dest := PtrOffs(Dest, MaxI(0, -Rect.Left * ProcessingFormatBpP));
  1085.   Rect.Left   := ClampI(Rect.Left,   0, LevelInfo[ALevel].Width);
  1086.   Rect.Right  := ClampI(Rect.Right,  0, LevelInfo[ALevel].Width);
  1087.   Rect.Top    := ClampI(Rect.Top,    0, LevelInfo[ALevel].Height);
  1088.   Rect.Bottom := ClampI(Rect.Bottom, 0, LevelInfo[ALevel].Height);
  1089.   w := Rect.Right-Rect.Left;
  1090.   Assert(w <= MaxLineLength, 'TMegaImageResource.LoadRectAsRGBA: Line length is too big');
  1091.   for i := Rect.Top to Rect.Bottom-1 do
  1092.     if not LoadSeq(Rect.Left, i, w, ALevel, @Temp) or
  1093.        not ConvertToProcessing(Format, w, @Temp[0], 0, nil, PtrOffs(Dest, ((i-Rect.Top)*DestImageWidth)*ProcessingFormatBpP)) then Exit;
  1094.   Result := True;
  1095. end;
  1096. function TMegaImageResource.SaveSeq(AX, AY, ALength, ALevel: Integer; Src: Pointer): Boolean;
  1097. var BlockX, BlockY, BlockXOfs, BlockYOfs, BpP, Len, abw: Integer; Temp: Pointer;
  1098. begin
  1099.   Result := False;
  1100.   if (ActualBlockWidth < MinBlockSize) or (ActualBlockHeight < MinBlockSize) then Exit;
  1101.   BpP := GetBytesPerPixel(Format);
  1102.   BlockX := AX div ActualBlockWidth;
  1103.   BlockY := AY div ActualBlockHeight;
  1104.   BlockXOfs := AX - BlockX * ActualBlockWidth;
  1105.   BlockYOfs := AY - BlockY * ActualBlockHeight;
  1106.   abw := MinI(LevelInfo[ALevel].Width, ActualBlockWidth);
  1107. //  Log.Log(SysUtils.Format('*** (%D, %D), Bl (%D, %D) ', [AX, AY, BlockX, BlockY]));
  1108.   while ALength > 0 do begin
  1109.     Temp := GetBlockData(ALevel, BlockX, BlockY);
  1110.     if Temp = nil then Exit;
  1111.     Len := MinI(abw - BlockXOfs, ALength);
  1112.     Move(Src^, PtrOffs(Temp, (BlockYOfs * abw + BlockXOfs)*BpP)^, Len*BpP);
  1113.     SaveBlockData(ALevel, BlockX, BlockY);
  1114.     Dec(ALength, Len);
  1115.     Src := PtrOffs(Src, Len * BpP);
  1116.     BlockXOfs := 0;
  1117.     Inc(BlockX);
  1118.   end;
  1119.   Result := True;
  1120. end;
  1121. function TMegaImageResource.SaveRect(Rect: TRect; ALevel: Integer; Src: Pointer; SrcImageWidth: Integer; BuildMips: Boolean): Boolean;
  1122. var i, ow, oh, nw, nh: Integer; Temp, Temp2: Pointer; NewRect: TRect;
  1123. begin
  1124.   Result := False;
  1125.   Rect.Left   := ClampI(Rect.Left,   0, LevelInfo[ALevel].Width);
  1126.   Rect.Right  := ClampI(Rect.Right,  0, LevelInfo[ALevel].Width);
  1127.   Rect.Top    := ClampI(Rect.Top,    0, LevelInfo[ALevel].Height);
  1128.   Rect.Bottom := ClampI(Rect.Bottom, 0, LevelInfo[ALevel].Height);
  1129.   for i := Rect.Top to Rect.Bottom-1 do
  1130.     if not SaveSeq(Rect.Left, i, Rect.Right-Rect.Left, ALevel, PtrOffs(Src, ((i-Rect.Top)*SrcImageWidth+Rect.Left - Rect.Left)*FBitsPerPixel div 8)) then Exit;
  1131.   if BuildMips and (ALevel < ActualLevels-1) then begin
  1132.     Rect := GetRectIntersect(GetRectExpanded(Rect, Ceil(MinFilterParameter), Ceil(MinFilterParameter)), GetRect(0, 0, LevelInfo[ALevel].Width, LevelInfo[ALevel].Height));
  1133.     ow := Rect.Right  - Rect.Left;
  1134.     oh := Rect.Bottom - Rect.Top;
  1135.     GetMem(Temp, ow*oh * FBitsPerPixel div 8);
  1136.     LoadRect(Rect, ALevel, Temp, ow);
  1137.     RectScale(Rect, 0.5, 0.5, NewRect);
  1138.     nw := NewRect.Right  - NewRect.Left;
  1139.     nh := NewRect.Bottom - NewRect.Top;
  1140.     GetMem(Temp2, nw*nh * FBitsPerPixel div 8);
  1141.     Base2D.ResizeImage(MinFilter, MinFilterParameter, Format, Temp,  GetRect(0, 0, ow, oh), ow,
  1142.                                                               Temp2, GetRect(0, 0, nw, nh), nw);
  1143.     SaveRect(NewRect, ALevel+1, Temp2, nw, True);
  1144.     FreeMem(Temp2); FreeMem(Temp);
  1145.   end;
  1146.   Result := True;
  1147. end;
  1148. { TMegaImagePaintOp }
  1149. procedure TMegaImagePaintOp.DoApply;
  1150. begin
  1151.   FResource.LoadRect(FRect, FLevel, FTempData, FRect.Right-FRect.Left);
  1152.   FResource.SaveRect(FRect, FLevel, FData, FRect.Right-FRect.Left, True);
  1153.   Move(FTempData^, FData^, (FRect.Bottom-FRect.Top) * (FRect.Right-FRect.Left) * FImageBpP);
  1154. end;
  1155. constructor TMegaImagePaintOp.Create(X, Y: Integer; AResource: TMegaImageResource; ALevel: Integer; ABrush: TBrush; const ARect: BaseTypes.TRect);
  1156. begin
  1157.   Assert(Assigned(AResource), 'TMegaImagePaintOp.Create: Resource is undefined');
  1158.   inherited Create(nil, AResource.Width, AResource.Format, GetRectIntersect(GetRect(X, Y, X+ABrush.Width, Y+ABrush.Height), ARect));
  1159.   FResource := AResource;
  1160.   FLevel    := ALevel;
  1161.   GetMem(FTempData, (FRect.Bottom-FRect.Top) * (FRect.Right-FRect.Left) * FImageBpP);
  1162.   FResource.LoadRect(Rect, FLevel, FData, FRect.Right-FRect.Left);
  1163.   BufferRGBABlend(PtrOffs(ABrush.PatternData, (MaxI(0, -Y) * ABrush.Width + MaxI(0, -X)) * SizeOf(TColor)),
  1164.                   FData,
  1165.                   PtrOffs(ABrush.ShapeData, (MaxI(0, -Y) * ABrush.Width + MaxI(0, -X))),
  1166.                   ABrush.Width, Rect.Right-Rect.Left, FImageFormat, GetRectMoved(Rect, -Rect.Left, -Rect.Top));
  1167. //  BufferRGBACombine(PtrOffs(ABrush.ShapeData, (MaxI(0, -Y) * ABrush.Width + MaxI(0, -X)) * SizeOf(TColor)),
  1168. //                    FData, ABrush.Width, Rect.Right-Rect.Left, FImageFormat, GetRectMove(Rect, -Rect.Left, -Rect.Top));
  1169. end;
  1170. destructor TMegaImagePaintOp.Destroy;
  1171. begin
  1172.   FreeMem(FTempData);
  1173.   inherited;
  1174. end;
  1175. { TMegaImageSource }
  1176. function TMegaImageSource.GetData(const Rect: TRect; Dest: Pointer; DestImageWidth: Integer): Boolean;
  1177. begin
  1178.   Result := FResource.LoadRect(Rect, FLevel, Dest, DestImageWidth);
  1179. end;
  1180. function TMegaImageSource.GetDataAsRGBA(const Rect: TRect; Dest: Pointer; DestImageWidth: Integer): Boolean;
  1181. begin
  1182.   Result := FResource.LoadRectAsRGBA(Rect, FLevel, Dest, DestImageWidth);
  1183. end;
  1184. constructor TMegaImageSource.Create(AResource: TMegaImageResource; ALevel: Integer);
  1185. begin
  1186.   Assert(Assigned(AResource));
  1187.   inherited Create(AResource.Format, AResource.LevelInfo[ALevel].Width, AResource.LevelInfo[ALevel].Height);
  1188.   FResource := AResource;
  1189.   FLevel    := ALevel;
  1190. end;
  1191. begin
  1192.   GlobalClassList.Add('Resources', GetUnitClassList);
  1193. end.