Advertisement
Guest User

Untitled

a guest
Apr 23rd, 2011
147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 15.09 KB | None | 0 0
  1. { This unit implments a subset of the Flexible Image Transport System
  2.   (the FITS file format) for Lazarus / Free Pascal.
  3.   standards document: http://fits.gsfc.nasa.gov/fits_standard.html
  4.  
  5.   An object of type TFitsObject can be created for example from a TBitmap,
  6.   additional headers or data can be added or existing ones modified and
  7.   the object can be written as a FITS file to a stream or to a file.
  8.   Reading and parsing an existing FITS file is not yet implemented.
  9.  
  10.   License is GNU LGPL 2 with FPC/LCL linking exception (see below).
  11.  
  12.   ***
  13.  
  14.   Copyright (C) 2011 Bernd Kreuss <prof7bit@googlemail.com>
  15.  
  16.   This library is free software; you can redistribute it and/or modify it
  17.   under the terms of the GNU Library General Public License as published by
  18.   the Free Software Foundation; either version 2 of the License, or (at your
  19.   option) any later version with the following modification:
  20.  
  21.   As a special exception, the copyright holders of this library give you
  22.   permission to link this library with independent modules to produce an
  23.   executable, regardless of the license terms of these independent modules,and
  24.   to copy and distribute the resulting executable under terms of your choice,
  25.   provided that you also meet, for each linked independent module, the terms
  26.   and conditions of the license of that module. An independent module is a
  27.   module which is not derived from or based on this library. If you modify
  28.   this library, you may extend this exception to your version of the library,
  29.   but you are not obligated to do so. If you do not wish to do so, delete this
  30.   exception statement from your version.
  31.  
  32.   This program is distributed in the hope that it will be useful, but WITHOUT
  33.   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  34.   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  35.   for more details.
  36.  
  37.   You should have received a copy of the GNU Library General Public License
  38.   along with this library; if not, write to the Free Software Foundation,
  39.   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  40. }
  41.  
  42. unit FitsObject;
  43.  
  44. {$mode objfpc}{$H+}
  45.  
  46. interface
  47.  
  48. uses
  49.   Classes, SysUtils, Graphics;
  50.  
  51. type
  52.   { EFitsInvalidData will be raised when composing a FITS object
  53.   and wrong or invalid data is passed into one of the methods,
  54.   for example strings are too long, etc. }
  55.   EFitsInvalidData = Class(Exception);
  56.  
  57.   { TFitsHeaderCard (also often referred to as ascii card image) is an
  58.   exactly 80 byte ascii string and contains one key-value pair. Many of
  59.   these header cards will be contained in an header block. The name
  60.   "card image" comes from the old days of punched cards where IBM cards
  61.   were exactly 80 characters in length. }
  62.   TFitsHeaderCard = Class(TObject)
  63.     constructor Create(AKey: String; AValue: String; AddQuotes: Boolean);
  64.     constructor Create(AKey: String; AValue: Integer);
  65.     constructor Create(AKey: String; AValue: Extended);
  66.     procedure SetKey(AKey: String);
  67.     procedure SetValue(AValue: String; AddQuotes: Boolean);
  68.     procedure SetValue(AValue: Integer);
  69.     procedure SetValue(AValue: Extended);
  70.     function GetKey: String;
  71.     function GetValue: String;
  72.     procedure SaveToStream(AStream: TStream);
  73.   private
  74.     FText : array[1..80] of Char;
  75.   end;
  76.  
  77.   { TFitsHeaderBlock contains any amount of headers. The standard defines
  78.   a header block as having exacty 2880 bytes length padded with 0x20,
  79.   followed by additional header blocks of the same size if needed. For
  80.   the sake of simplicity in this library we are defining the header block
  81.   as having an arbitrary amount of header records (cards) and there is only
  82.   one such block per HDU when the FITS object is in memory and the
  83.   splitting and padding into 2880 byte blocks will only happen later when
  84.   writing it to the stream}
  85.   TFitsHeaderBlock = Class(TObject)
  86.     constructor Create;
  87.     destructor Destroy; override;
  88.     procedure AddStdCards(BITPIX, DATAMAX, NAXIS1, NAXIS2: Integer; Gray: Boolean);
  89.     procedure AddCard(AHeader: TFitsHeaderCard);
  90.     function GetCard(AKey: String): TFitsHeaderCard;
  91.     procedure SaveToStream(AStream: TStream);
  92.   private
  93.     FCards: array of TFitsHeaderCard;
  94.   end;
  95.  
  96.   { TFitsDataBlock contains data}
  97.   TFitsDataBlock = Class(TObject)
  98.     constructor Create;
  99.     constructor Create8x3From24Bitmap(ABitmap: TBitmap);
  100.     constructor Create8x1From24Bitmap(ABitmap: TBitmap);
  101.     destructor Destroy; override;
  102.     procedure SaveToStream(AStream: TStream);
  103.   private
  104.     FData: Pointer;
  105.     FSize: Integer;
  106.   end;
  107.  
  108.   { TFitsHDU (HDU, Header-and-Data-Unit) contains a header block and a
  109.   data block. There is at least one such HDU in each FITS file, the first
  110.   one is called the primary HDU}
  111.   TFitsHDU = Class(TObject)
  112.     constructor Create;
  113.     destructor Destroy; override;
  114.     procedure SaveToStream(AStream: TStream);
  115.   private
  116.     FHeaderBlock: TFitsHeaderBlock;
  117.     FDataBlock: TFitsDataBlock;
  118.   public
  119.     property HeaderBlock: TFitsHeaderBlock read FHeaderBlock;
  120.     property DataBlock: TFitsDatablock read FDataBlock write FDataBlock;
  121.   end;
  122.  
  123.   { TFitsObject is repesenting a FITS object which consists of
  124.     one or more HDU objects}
  125.   TFitsObject = class(TObject)
  126.     constructor Create;
  127.     destructor Destroy; override;
  128.     constructor CreateFromBitmap(ABitmap: TBitmap);
  129.     constructor CreateFromFile(AName: String);
  130.     constructor CreateFromStream(AStream: TStream);
  131.     procedure SaveToFile(AName: String);
  132.     procedure SaveToStream(AStream: TStream);
  133.   private
  134.     FHDUs : array of TFitsHDU;
  135.   public
  136.     property Primary: TFitsHDU read FHDUs[0];
  137.   end;
  138.  
  139. implementation
  140.  
  141. { TFitsHeaderCard }
  142.  
  143. constructor TFitsHeaderCard.Create(AKey: String; AValue: String; AddQuotes: Boolean);
  144. begin
  145.   Inherited Create;
  146.   SetKey(AKey);
  147.   SetValue(AValue, AddQuotes);
  148. end;
  149.  
  150. constructor TFitsHeaderCard.Create(AKey: String; AValue: Integer);
  151. begin
  152.   Create(AKey, IntToStr(AValue), False);
  153. end;
  154.  
  155. constructor TFitsHeaderCard.Create(AKey: String; AValue: Extended);
  156. begin
  157.   Create(AKey, FloatToStr(AValue), False);
  158. end;
  159.  
  160. procedure TFitsHeaderCard.SetKey(AKey: String);
  161. var
  162.   i, k: Integer;
  163. begin
  164.   if Length(AKey) > 8 then
  165.     raise EFitsInvalidData.Create('header key exceeding 8 byte');
  166.  
  167.   AKey := UpperCase(AKey);
  168.   for i := 1 to Length(AKey) do begin
  169.     FText[i] := AKey[i];
  170.   end;
  171.   for k := i+1 to 8 do begin
  172.     FText[k] := ' ';
  173.   end;
  174. end;
  175.  
  176. procedure TFitsHeaderCard.SetValue(AValue: String; AddQuotes: Boolean);
  177. var
  178.   i, k: Integer;
  179. begin
  180.   if AddQuotes then
  181.     AValue := '''' + AValue + '''';
  182.   if Length(AValue) > 70 then
  183.     raise EFitsInvalidData.Create('header value exceeding 70 byte');
  184.  
  185.   FText[9] := '=';
  186.   FText[10] := ' ';
  187.  
  188.   for i := 1 to Length(AValue) do begin
  189.     FText[10+i] := AValue[i];
  190.   end;
  191.   for k := i+1 to 70 do begin
  192.     FText[10+k] := ' ';
  193.   end;
  194. end;
  195.  
  196. procedure TFitsHeaderCard.SetValue(AValue: Integer);
  197. begin
  198.   SetValue(IntToStr(AValue), False);
  199. end;
  200.  
  201. procedure TFitsHeaderCard.SetValue(AValue: Extended);
  202. begin
  203.   SetValue(FloatToStr(AValue), False);
  204. end;
  205.  
  206. function TFitsHeaderCard.GetKey: String;
  207. begin
  208.   Result := Trim(LeftStr(FText, 8));
  209. end;
  210.  
  211. function TFitsHeaderCard.GetValue: String;
  212. begin
  213.   Result := Trim(RightStr(FText, 70));
  214.   if Length(Result) > 1 then begin
  215.     if Result[1] = '''' then begin
  216.       // return quoted strings without quotes
  217.       Result := RightStr(Result, Length(Result) - 1);
  218.       Result := LeftStr(Result, Length(Result) - 1);
  219.     end;
  220.   end;
  221. end;
  222.  
  223. procedure TFitsHeaderCard.SaveToStream(AStream: TStream);
  224. begin
  225.   AStream.WriteBuffer(FText, SizeOf(FText));
  226. end;
  227.  
  228.  
  229. { TFitsHeaderBlock }
  230.  
  231. constructor TFitsHeaderBlock.Create;
  232. begin
  233.   Inherited Create;
  234.  
  235. end;
  236.  
  237. destructor TFitsHeaderBlock.Destroy;
  238. var
  239.   Card: TFitsHeaderCard;
  240. begin
  241.   if Length(FCards) > 0 then begin
  242.     for Card in FCards do begin
  243.       Card.Free;
  244.     end;
  245.   end;
  246.   SetLength(FCards, 0);
  247.   inherited Destroy;
  248. end;
  249.  
  250. procedure TFitsHeaderBlock.AddStdCards(BITPIX, DATAMAX, NAXIS1, NAXIS2: Integer; Gray: Boolean);
  251. var
  252.   Naxis: Integer;
  253. begin
  254.   if not BITPIX in [8, 16] then
  255.     raise EFitsInvalidData.Create('Invalid BITPIX value');
  256.   if (DATAMAX >= (1 << BITPIX)) or (DATAMAX < 1) then
  257.     raise EFitsInvalidData.Create('Invalid DATAMAX value');
  258.   if Gray then
  259.     Naxis := 2
  260.   else
  261.     Naxis := 3;
  262.  
  263.   AddCard(TFitsHeaderCard.Create('BITPIX', BITPIX));
  264.   AddCard(TFitsHeaderCard.Create('NAXIS', Naxis));
  265.   AddCard(TFitsHeaderCard.Create('NAXIS1', NAXIS1));
  266.   AddCard(TFitsHeaderCard.Create('NAXIS2', NAXIS2));
  267.   if Naxis = 3 then
  268.     AddCard(TFitsHeaderCard.Create('NAXIS3', 3)); // 3 colors
  269.   AddCard(TFitsHeaderCard.Create('BSCALE', 1.0));
  270.   AddCard(TFitsHeaderCard.Create('BZERO', 0.0));
  271.   AddCard(TFitsHeaderCard.Create('DATAMAX', DATAMAX));
  272.   AddCard(TFitsHeaderCard.Create('DATAMIN', 0));
  273.   AddCard(TFitsHeaderCard.Create('MIPS-HI', DATAMAX)); // this initializes
  274.   AddCard(TFitsHeaderCard.Create('MIPS-LO', 0));       // the visu of IRIS
  275. end;
  276.  
  277. procedure TFitsHeaderBlock.AddCard(AHeader: TFitsHeaderCard);
  278. var
  279.   Cnt : Integer;
  280. begin
  281.   Cnt := Length(FCards);
  282.   SetLength(FCards, Cnt+1);
  283.   FCards[Cnt] := AHeader;
  284. end;
  285.  
  286. function TFitsHeaderBlock.GetCard(AKey: String): TFitsHeaderCard;
  287. var
  288.   Card: TFitsHeaderCard;
  289. begin
  290.   Result := nil;
  291.   for Card in FCards do begin
  292.     if Card.GetKey = AKey then begin
  293.       Result := Card;
  294.       break;
  295.     end;
  296.   end;
  297. end;
  298.  
  299. procedure TFitsHeaderBlock.SaveToStream(AStream: TStream);
  300. var
  301.   Card: TFitsHeaderCard;
  302.   Bytes: Integer = 0;
  303.   Remaining: Integer;
  304.   i: Integer;
  305.   sEND : array[0..2] of Char = 'END';
  306. begin
  307.   // the standard demands that the headers are written in blocks
  308.   // of 2880 bytes (this is exactly 36 cards). Since full blocks
  309.   // would fit together seamlessly we can just write all cards
  310.   // each after the other without interruption.
  311.   for Card in FCards do begin
  312.     Card.SaveToStream(AStream);
  313.     Bytes += 80;
  314.   end;
  315.  
  316.   // write the mandatory END card (we haven't stored this in the
  317.   // FCards array since it is not a real key-value pair, its more
  318.   // like a marker that is always there but carries no payload.
  319.   AStream.Write(sEND, 3);
  320.   for i:=1 to 77 do begin
  321.     AStream.WriteByte($20);
  322.   end;
  323.   Bytes += 80;
  324.  
  325.   // When done with all cards we must pad with 0x20 until the
  326.   // total of written bytes is a multiple of 2880.
  327.   Remaining := 2880 - (Bytes mod 2880);
  328.   for i:=1 to Remaining do begin
  329.     AStream.WriteByte($20);
  330.   end;
  331. end;
  332.  
  333.  
  334. { TFitsDataBlock }
  335.  
  336. constructor TFitsDataBlock.Create;
  337. begin
  338.   Inherited Create;
  339. end;
  340.  
  341. constructor TFitsDataBlock.Create8x3From24Bitmap(ABitmap: TBitmap);
  342. var
  343.   x,y: Integer;
  344.   DestPtr: PByte;    // the size in a BITPIX = 8 is one byte per value
  345.   SrcPtr: PInteger;  // the size of an RGB pixel in the source bitmap
  346.   BytesPerLine: Integer;
  347. begin
  348.   FSize := ABitmap.Width * ABitmap.Height * 3;
  349.   FData := Getmem(FSize);
  350.  
  351.   BytesPerLine := ABitmap.RawImage.Description.BitsPerLine div 8;
  352.   DestPtr := FData;
  353.  
  354.   // red
  355.   for y:= ABitmap.Height-1 downto 0 do begin
  356.     SrcPtr := PInteger(ABitmap.RawImage.Data + BytesPerLine * y);
  357.     for x:=0 to ABitmap.Width-1 do begin
  358.       DestPtr^ := (SrcPtr^ >> 16) and $ff;
  359.       DestPtr += 1;
  360.       SrcPtr += 1;
  361.     end;
  362.   end;
  363.  
  364.   // green
  365.   for y:= ABitmap.Height-1 downto 0 do begin
  366.     SrcPtr := PInteger(ABitmap.RawImage.Data + BytesPerLine * y);
  367.     for x:=0 to ABitmap.Width-1 do begin
  368.       DestPtr^ := (SrcPtr^ >> 8) and $ff;
  369.       DestPtr += 1;
  370.       SrcPtr += 1;
  371.     end;
  372.   end;
  373.  
  374.   // blue
  375.   for y:= ABitmap.Height-1 downto 0 do begin
  376.     SrcPtr := PInteger(ABitmap.RawImage.Data + BytesPerLine * y);
  377.     for x:=0 to ABitmap.Width-1 do begin
  378.       DestPtr^ := SrcPtr^ and $ff;
  379.       DestPtr += 1;
  380.       SrcPtr += 1;
  381.     end;
  382.   end;
  383. end;
  384.  
  385. constructor TFitsDataBlock.Create8x1From24Bitmap(ABitmap: TBitmap);
  386. var
  387.   x,y: Integer;
  388.   DestPtr: PByte;    // the size in a BITPIX = 8 is one byte per value
  389.   SrcPtr: PInteger;  // the size of an RGB pixel in the source bitmap
  390.   BytesPerLine: Integer;
  391. begin
  392.   FSize := ABitmap.Width * ABitmap.Height;
  393.   FData := Getmem(FSize);
  394.  
  395.   BytesPerLine := ABitmap.RawImage.Description.BitsPerLine div 8;
  396.   DestPtr := FData;
  397.  
  398.   for y:= ABitmap.Height-1 downto 0 do begin
  399.     SrcPtr := PInteger(ABitmap.RawImage.Data + BytesPerLine * y);
  400.     for x:=0 to ABitmap.Width-1 do begin
  401.       DestPtr^ := (
  402.                   ((SrcPtr^ >> 16) and $ff) +
  403.                   ((SrcPtr^ >> 8) and $ff) +
  404.                   (SrcPtr^ and $ff)
  405.                 ) div 3;
  406.       DestPtr += 1;
  407.       SrcPtr += 1;
  408.     end;
  409.   end;
  410. end;
  411.  
  412. destructor TFitsDataBlock.Destroy;
  413. begin
  414.   if FData <> Nil then
  415.     Freemem(FData, FSize);
  416.   inherited Destroy;
  417. end;
  418.  
  419. procedure TFitsDataBlock.SaveToStream(AStream: TStream);
  420. var
  421.   Remaining: Integer;
  422.   i: Integer;
  423. begin
  424.   AStream.WriteBuffer(FData^, FSize);
  425.  
  426.   // padding with $00 to multiple of 2880
  427.   Remaining := 2880 - (FSize mod 2880);
  428.   for i:=1 to Remaining do begin
  429.     AStream.WriteByte(0);
  430.   end;
  431. end;
  432.  
  433.  
  434. { TFitsHDU }
  435.  
  436. constructor TFitsHDU.Create;
  437. begin
  438.   Inherited Create;
  439.   FHeaderBlock := TFitsHeaderBlock.Create;
  440.   FDataBlock := nil // data block is optional, can be created later
  441. end;
  442.  
  443. destructor TFitsHDU.Destroy;
  444. begin
  445.   FreeAndNil(FHeaderBlock);
  446.   if FDataBlock <> nil then FreeAndNil(FDataBlock);
  447.   inherited Destroy;
  448. end;
  449.  
  450. procedure TFitsHDU.SaveToStream(AStream: TStream);
  451. begin
  452.   FHeaderBlock.SaveToStream(AStream);
  453.   if FDataBlock <> nil then FDataBlock.SaveToStream(AStream);
  454. end;
  455.  
  456.  
  457. { TFitsObject }
  458.  
  459. constructor TFitsObject.Create;
  460. begin
  461.   Inherited Create;
  462.  
  463.   // create the mandatory pimary HDU
  464.   SetLength(FHDUs, 1);
  465.   FHDUs[0] := TFitsHDU.Create;
  466.  
  467.   // this is now automatically also accessible via the property 'Primary'
  468.   // and now we add the for all FITS mandatory card 'SIMPLE = T'
  469.   Primary.HeaderBlock.AddCard(TFitsHeaderCard.Create('SIMPLE', '                   T', False));
  470. end;
  471.  
  472. destructor TFitsObject.Destroy;
  473. var
  474.   HDU: TFitsHDU;
  475. begin
  476.   for HDU in FHDUs do begin
  477.     HDU.Free;
  478.   end;
  479.   SetLength(FHDUs, 0);
  480.   inherited Destroy;
  481. end;
  482.  
  483. constructor TFitsObject.CreateFromBitmap(ABitmap: TBitmap);
  484. begin
  485.   Create;
  486.   Primary.HeaderBlock.AddStdCards(8, 255, ABitmap.Width, ABitmap.Height, False);
  487.   Primary.DataBlock := TFitsDataBlock.Create8x3From24Bitmap(ABitmap);
  488. end;
  489.  
  490. constructor TFitsObject.CreateFromFile(AName: String);
  491. var
  492.   Stream: TFileStream;
  493. begin
  494.   Create;
  495.  
  496.   raise EAbstractError.Create('sorry, this is not yet impmemented');
  497.  
  498.   Stream := TFileStream.Create(AName, fmOpenRead);
  499.   CreateFromStream(Stream);
  500.   Stream.Free;
  501. end;
  502.  
  503. constructor TFitsObject.CreateFromStream(AStream: TStream);
  504. begin
  505.   Create;
  506.  
  507.   raise EAbstractError.Create('sorry, this is not yet impmemented');
  508. end;
  509.  
  510. procedure TFitsObject.SaveToFile(AName: String);
  511. var
  512.   Stream : TFileStream;
  513. begin
  514.   Stream := TFileStream.Create(AName, fmCreate);
  515.   SaveToStream(Stream);
  516.   Stream.Free;
  517. end;
  518.  
  519. procedure TFitsObject.SaveToStream(AStream: TStream);
  520. var
  521.   HDU: TFitsHDU;
  522. begin
  523.   for HDU in FHDUs do begin
  524.     HDU.SaveToStream(AStream);
  525.   end;
  526. end;
  527.  
  528. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement