Advertisement
Guest User

Untitled

a guest
Dec 21st, 2012
1,287
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 19.42 KB | None | 0 0
  1. unit resample;
  2. // -----------------------------------------------------------------------------
  3. // Project: bitmap resampler
  4. // Module:  resample
  5. // Description: Interpolated Bitmap Resampling using filters.
  6. // Version: 01.02
  7. // Release: 3
  8. // Date:    15-MAR-1998
  9. // Target:  Win32, Delphi 2 & 3
  10. // Author(s):   anme: Anders Melander, [email protected]
  11. // Copyright    (c) 1997,98 by Anders Melander
  12. // Formatting:  2 space indent, 8 space tabs, 80 columns.
  13. // -----------------------------------------------------------------------------
  14. // This software is copyrighted as noted above.  It may be freely copied,
  15. // modified, and redistributed, provided that the copyright notice(s) is
  16. // preserved on all copies.
  17. //
  18. // There is no warranty or other guarantee of fitness for this software,
  19. // it is provided solely "as is".  Bug reports or fixes may be sent
  20. // to the author, who may or may not act on them as he desires.
  21. //
  22. // You may not include this software in a program or other software product
  23. // without supplying the source, or without informing the end-user that the
  24. // source is available for no extra charge.
  25. //
  26. // If you modify this software, you should include a notice in the "Revision
  27. // history" section giving the name of the person performing the modification,
  28. // the date of modification, and the reason for such modification.
  29. // -----------------------------------------------------------------------------
  30. // Here's some additional copyrights for you:
  31. //
  32. // From filter.c:
  33. // The authors and the publisher hold no copyright restrictions
  34. // on any of these files; this source code is public domain, and
  35. // is freely available to the entire computer graphics community
  36. // for study, use, and modification.  We do request that the
  37. // comment at the top of each file, identifying the original
  38. // author and its original publication in the book Graphics
  39. // Gems, be retained in all programs that use these files.
  40. //
  41. // -----------------------------------------------------------------------------
  42. // Revision history:
  43. //
  44. // 0100 110997  anme    - Adapted from fzoom v0.20 by Dale Schumacher.
  45. //
  46. // 0101 110198  anme    - Added Lanczos3 and Mitchell filters.
  47. //          - Fixed range bug.
  48. //            Min value was not checked on conversion from Single to
  49. //            byte.
  50. //          - Numerous optimizations.
  51. //          - Added TImage stretch on form resize.
  52. //          - Added support for Delphi 2 via TCanvas.Pixels.
  53. //          - Renamed module from stretch to resample.
  54. //          - Moved demo code to separate module.
  55. //
  56. // 0102 150398  anme    - Fixed a problem that caused all pixels to be shifted
  57. //            1/2 pixel down and to the right (in source
  58. //            coordinates). Thanks to David Ullrich for the
  59. //            solution.
  60. // -----------------------------------------------------------------------------
  61. // Credits:
  62. // The algorithms and methods used in this library are based on the article
  63. // "General Filtered Image Rescaling" by Dale Schumacher which appeared in the
  64. // book Graphics Gems III, published by Academic Press, Inc.
  65. //
  66. // The edge offset problem was fixed by:
  67. //   * David Ullrich <[email protected]>
  68. // -----------------------------------------------------------------------------
  69. // To do (in rough order of priority):
  70. // * Implement Dale Schumacher's "Optimized Bitmap Scaling Routines".
  71. // * Fix BoxFilter.
  72. // * Optimize to use integer math instead of floating point where possible.
  73. // -----------------------------------------------------------------------------
  74. interface
  75.  
  76. // If USE_SCANLINE is defined, Stretch will use the TBitmap.Scanline property
  77. // instead of TBitmap.Canvas.Pixels to access the bitmap pixels.
  78. // Use of the Scanline property is 20 to 50 times faster than the Pixels
  79. // property!
  80.  
  81. {$DEFINE USE_SCANLINE}
  82.  
  83. uses
  84.   SysUtils, Classes, Graphics;
  85.  
  86. type
  87.   // Type of a filter for use with Stretch()
  88.   TFilterProc = function(Value: Single): Single;
  89.  
  90.   // Sample filters for use with Stretch()
  91.   function SplineFilter(Value: Single): Single;
  92.   function BellFilter(Value: Single): Single;
  93.   function TriangleFilter(Value: Single): Single;
  94.   function BoxFilter(Value: Single): Single;
  95.   function HermiteFilter(Value: Single): Single;
  96.   function Lanczos3Filter(Value: Single): Single;
  97.   function MitchellFilter(Value: Single): Single;
  98.  
  99.   // Interpolator
  100.   // Src:   Source bitmap
  101.   // Dst:   Destination bitmap
  102.   // filter:    Weight calculation filter
  103.   // fwidth:    Relative sample radius
  104.   procedure Strecth(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single);
  105.  
  106. // -----------------------------------------------------------------------------
  107. //
  108. //          List of Filters
  109. //
  110. // -----------------------------------------------------------------------------
  111.  
  112. const
  113.   ResampleFilters: array[0..6] of record
  114.     Name: string;   // Filter name
  115.     Filter: TFilterProc;// Filter implementation
  116.     Width: Single;  // Suggested sampling width/radius
  117.   end = (
  118.     (Name: 'Box';   Filter: BoxFilter;  Width: 0.5),
  119.     (Name: 'Triangle';  Filter: TriangleFilter; Width: 1.0),
  120.     (Name: 'Hermite';   Filter: HermiteFilter;  Width: 1.0),
  121.     (Name: 'Bell';  Filter: BellFilter; Width: 1.5),
  122.     (Name: 'B-Spline';  Filter: SplineFilter;   Width: 2.0),
  123.     (Name: 'Lanczos3';  Filter: Lanczos3Filter; Width: 3.0),
  124.     (Name: 'Mitchell';  Filter: MitchellFilter; Width: 2.0)
  125.     );
  126.  
  127. implementation
  128.  
  129. uses
  130.   math;
  131.  
  132. // -----------------------------------------------------------------------------
  133. //
  134. //          Filter functions
  135. //
  136. // -----------------------------------------------------------------------------
  137.  
  138. // Hermite filter
  139. function HermiteFilter(Value: Single): Single;
  140. begin
  141.   // f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
  142.   if (Value < 0.0) then
  143.     Value := -Value;
  144.   if (Value < 1.0) then
  145.     Result := (2.0 * Value - 3.0) * Sqr(Value) + 1.0
  146.   else
  147.     Result := 0.0;
  148. end;
  149.  
  150. // Box filter
  151. // a.k.a. "Nearest Neighbour" filter
  152. // anme: I have not been able to get acceptable
  153. //       results with this filter for subsampling.
  154. function BoxFilter(Value: Single): Single;
  155. begin
  156.   if (Value > -0.5) and (Value <= 0.5) then
  157.     Result := 1.0
  158.   else
  159.     Result := 0.0;
  160. end;
  161.  
  162. // Triangle filter
  163. // a.k.a. "Linear" or "Bilinear" filter
  164. function TriangleFilter(Value: Single): Single;
  165. begin
  166.   if (Value < 0.0) then
  167.     Value := -Value;
  168.   if (Value < 1.0) then
  169.     Result := 1.0 - Value
  170.   else
  171.     Result := 0.0;
  172. end;
  173.  
  174. // Bell filter
  175. function BellFilter(Value: Single): Single;
  176. begin
  177.   if (Value < 0.0) then
  178.     Value := -Value;
  179.   if (Value < 0.5) then
  180.     Result := 0.75 - Sqr(Value)
  181.   else if (Value < 1.5) then
  182.   begin
  183.     Value := Value - 1.5;
  184.     Result := 0.5 * Sqr(Value);
  185.   end else
  186.     Result := 0.0;
  187. end;
  188.  
  189. // B-spline filter
  190. function SplineFilter(Value: Single): Single;
  191. var
  192.   tt            : single;
  193. begin
  194.   if (Value < 0.0) then
  195.     Value := -Value;
  196.   if (Value < 1.0) then
  197.   begin
  198.     tt := Sqr(Value);
  199.     Result := 0.5*tt*Value - tt + 2.0 / 3.0;
  200.   end else if (Value < 2.0) then
  201.   begin
  202.     Value := 2.0 - Value;
  203.     Result := 1.0/6.0 * Sqr(Value) * Value;
  204.   end else
  205.     Result := 0.0;
  206. end;
  207.  
  208. // Lanczos3 filter
  209. function Lanczos3Filter(Value: Single): Single;
  210.   function SinC(Value: Single): Single;
  211.   begin
  212.     if (Value <> 0.0) then
  213.     begin
  214.       Value := Value * Pi;
  215.       Result := sin(Value) / Value
  216.     end else
  217.       Result := 1.0;
  218.   end;
  219. begin
  220.   if (Value < 0.0) then
  221.     Value := -Value;
  222.   if (Value < 3.0) then
  223.     Result := SinC(Value) * SinC(Value / 3.0)
  224.   else
  225.     Result := 0.0;
  226. end;
  227.  
  228. function MitchellFilter(Value: Single): Single;
  229. const
  230.   B     = (1.0 / 3.0);
  231.   C     = (1.0 / 3.0);
  232. var
  233.   tt            : single;
  234. begin
  235.   if (Value < 0.0) then
  236.     Value := -Value;
  237.   tt := Sqr(Value);
  238.   if (Value < 1.0) then
  239.   begin
  240.     Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * tt))
  241.       + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
  242.       + (6.0 - 2 * B));
  243.     Result := Value / 6.0;
  244.   end else
  245.   if (Value < 2.0) then
  246.   begin
  247.     Value := (((-1.0 * B - 6.0 * C) * (Value * tt))
  248.       + ((6.0 * B + 30.0 * C) * tt)
  249.       + ((-12.0 * B - 48.0 * C) * Value)
  250.       + (8.0 * B + 24 * C));
  251.     Result := Value / 6.0;
  252.   end else
  253.     Result := 0.0;
  254. end;
  255.  
  256. // -----------------------------------------------------------------------------
  257. //
  258. //          Interpolator
  259. //
  260. // -----------------------------------------------------------------------------
  261. type
  262.   // Contributor for a pixel
  263.   TContributor = record
  264.     pixel: NativeInt;       // Source pixel
  265.     weight: single;     // Pixel weight
  266.   end;
  267.  
  268.   TContributorList = array[0..0] of TContributor;
  269.   PContributorList = ^TContributorList;
  270.  
  271.   // List of source pixels contributing to a destination pixel
  272.   TCList = record
  273.     n       : NativeInt;
  274.     p       : PContributorList;
  275.   end;
  276.  
  277.   TCListList = array[0..0] of TCList;
  278.   PCListList = ^TCListList;
  279.  
  280.   TRGB = packed record
  281.     r, g, b : single;
  282.   end;
  283.  
  284.   // Physical bitmap pixel
  285.   TColorRGB = packed record
  286.     r, g, b : BYTE;
  287.   end;
  288.   PColorRGB = ^TColorRGB;
  289.  
  290.   // Physical bitmap scanline (row)
  291.   TRGBList = packed array[0..0] of TColorRGB;
  292.   PRGBList = ^TRGBList;
  293.  
  294. procedure Strecth(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single);
  295. var
  296.   xscale, yscale    : single;       // Zoom scale factors
  297.   i, k      : longint;      // Loop variables
  298.   j: longint;
  299.   center        : single;       // Filter calculation variables
  300.   width, fscale, weight : single;       // Filter calculation variables
  301.   left, right       : longint;      // Filter calculation variables
  302.   n         : longint;      // Pixel number
  303.   Work          : TBitmap;
  304.   contrib       : PCListList;
  305.   rgb           : TRGB;
  306.   color         : TColorRGB;
  307. {$IFDEF USE_SCANLINE}
  308.   SourceLine        ,
  309.   DestLine      : PRGBList;
  310.   SourcePixel       ,
  311.   DestPixel     : PColorRGB;
  312.   Delta         ,
  313.   DestDelta     : NativeInt;
  314. {$ENDIF}
  315.   SrcWidth      ,
  316.   SrcHeight     ,
  317.   DstWidth      ,
  318.   DstHeight     : NativeInt;
  319.  
  320.   function Color2RGB(Color: TColor): TColorRGB;
  321.   begin
  322.     Result.r := Color AND $000000FF;
  323.     Result.g := (Color AND $0000FF00) SHR 8;
  324.     Result.b := (Color AND $00FF0000) SHR 16;
  325.   end;
  326.  
  327.   function RGB2Color(Color: TColorRGB): TColor;
  328.   begin
  329.     Result := Color.r OR (Color.g SHL 8) OR (Color.b SHL 16);
  330.   end;
  331.  
  332. begin
  333.   DstWidth := Dst.Width;
  334.   DstHeight := Dst.Height;
  335.   SrcWidth := Src.Width;
  336.   SrcHeight := Src.Height;
  337.   if (SrcWidth < 1) or (SrcHeight < 1) then
  338.     raise Exception.Create('Source bitmap too small');
  339.   // Create intermediate image to hold horizontal zoom
  340.   Work := TBitmap.Create;
  341.   try
  342.     Work.Height := SrcHeight;
  343.     Work.Width := DstWidth;
  344.     // xscale := DstWidth / SrcWidth;
  345.     // yscale := DstHeight / SrcHeight;
  346.     // Improvement suggested by David Ullrich:
  347.     if (SrcWidth = 1) then
  348.       xscale:= DstWidth / SrcWidth
  349.     else
  350.       xscale := (DstWidth - 1) / (SrcWidth - 1);
  351.     if (SrcHeight = 1) then
  352.       yscale := DstHeight / SrcHeight
  353.     else
  354.       yscale:= (DstHeight - 1) / (SrcHeight - 1);
  355.     // This implementation only works on 24-bit images because it uses
  356.     // TBitmap.Scanline
  357. {$IFDEF USE_SCANLINE}
  358.     Src.PixelFormat := pf24bit;
  359.     Dst.PixelFormat := Src.PixelFormat;
  360.     Work.PixelFormat := Src.PixelFormat;
  361. {$ENDIF}
  362.  
  363.     // --------------------------------------------
  364.     // Pre-calculate filter contributions for a row
  365.     // -----------------------------------------------
  366.     GetMem(contrib, DstWidth* sizeof(TCList));
  367.     // Horizontal sub-sampling
  368.     // Scales from bigger to smaller width
  369.     if (xscale < 1.0) then
  370.     begin
  371.       width := fwidth / xscale;
  372.       fscale := 1.0 / xscale;
  373.       for i := 0 to DstWidth-1 do
  374.       begin
  375.         contrib^[i].n := 0;
  376.         GetMem(contrib^[i].p, trunc(width * 2.0 + 1) * sizeof(TContributor));
  377.         center := i / xscale;
  378.         // Original code:
  379.         // left := ceil(center - width);
  380.         // right := floor(center + width);
  381.         left := floor(center - width);
  382.         right := ceil(center + width);
  383.         for j := left to right do
  384.         begin
  385.           weight := filter((center - j) / fscale) / fscale;
  386.           if (weight = 0.0) then
  387.             continue;
  388.           if (j < 0) then
  389.             n := -j
  390.           else if (j >= SrcWidth) then
  391.             n := SrcWidth - j + SrcWidth - 1
  392.           else
  393.             n := j;
  394.           k := contrib^[i].n;
  395.           contrib^[i].n := contrib^[i].n + 1;
  396.           contrib^[i].p^[k].pixel := n;
  397.           contrib^[i].p^[k].weight := weight;
  398.         end;
  399.       end;
  400.     end else
  401.     // Horizontal super-sampling
  402.     // Scales from smaller to bigger width
  403.     begin
  404.    for i := 0 to DstWidth-1 do
  405.       begin
  406.         contrib^[i].n := 0;
  407.         GetMem(contrib^[i].p, trunc(fwidth * 2.0 + 1) * sizeof(TContributor));
  408.         center := i / xscale;
  409.         // Original code:
  410.         // left := ceil(center - fwidth);
  411.         // right := floor(center + fwidth);
  412.         left := floor(center - fwidth);
  413.         right := ceil(center + fwidth);
  414.         for j := left to right do
  415.         begin
  416.           weight := filter(center - j);
  417.           if (weight = 0.0) then
  418.             continue;
  419.           if (j < 0) then
  420.             n := -j
  421.           else if (j >= SrcWidth) then
  422.             n := SrcWidth - j + SrcWidth - 1
  423.           else
  424.             n := j;
  425.           k := contrib^[i].n;
  426.           contrib^[i].n := contrib^[i].n + 1;
  427.           contrib^[i].p^[k].pixel := n;
  428.           contrib^[i].p^[k].weight := weight;
  429.         end;
  430.       end;
  431.     end;
  432.  
  433.     // ----------------------------------------------------
  434.     // Apply filter to sample horizontally from Src to Work
  435.     // ----------------------------------------------------
  436.     for k := 0 to SrcHeight-1 do
  437.     begin
  438. {$IFDEF USE_SCANLINE}
  439.       SourceLine := Src.ScanLine[k];
  440.       DestPixel := Work.ScanLine[k];
  441. {$ENDIF}
  442.       for i := 0 to DstWidth-1 do
  443.       begin
  444.         rgb.r := 0.0;
  445.         rgb.g := 0.0;
  446.         rgb.b := 0.0;
  447.         for j := 0 to contrib^[i].n-1 do
  448.         begin
  449. {$IFDEF USE_SCANLINE}
  450.           color := SourceLine^[contrib^[i].p^[j].pixel];
  451. {$ELSE}
  452.           color := Color2RGB(Src.Canvas.Pixels[contrib^[i].p^[j].pixel, k]);
  453. {$ENDIF}
  454.           weight := contrib^[i].p^[j].weight;
  455.           if (weight = 0.0) then
  456.             continue;
  457.           rgb.r := rgb.r + color.r * weight;
  458.           rgb.g := rgb.g + color.g * weight;
  459.           rgb.b := rgb.b + color.b * weight;
  460.         end;
  461.         if (rgb.r > 255.0) then
  462.           color.r := 255
  463.         else if (rgb.r < 0.0) then
  464.           color.r := 0
  465.         else
  466.           color.r := round(rgb.r);
  467.         if (rgb.g > 255.0) then
  468.           color.g := 255
  469.         else if (rgb.g < 0.0) then
  470.           color.g := 0
  471.         else
  472.           color.g := round(rgb.g);
  473.         if (rgb.b > 255.0) then
  474.           color.b := 255
  475.         else if (rgb.b < 0.0) then
  476.           color.b := 0
  477.         else
  478.           color.b := round(rgb.b);
  479. {$IFDEF USE_SCANLINE}
  480.         // Set new pixel value
  481.         DestPixel^ := color;
  482.         // Move on to next column
  483.         inc(DestPixel);
  484. {$ELSE}
  485.         Work.Canvas.Pixels[i, k] := RGB2Color(color);
  486. {$ENDIF}
  487.       end;
  488.     end;
  489.  
  490.     // Free the memory allocated for horizontal filter weights
  491.     for i := 0 to DstWidth-1 do
  492.       FreeMem(contrib^[i].p);
  493.  
  494.     FreeMem(contrib);
  495.  
  496.     // -----------------------------------------------
  497.     // Pre-calculate filter contributions for a column
  498.     // -----------------------------------------------
  499.     GetMem(contrib, DstHeight* sizeof(TCList));
  500.     // Vertical sub-sampling
  501.     // Scales from bigger to smaller height
  502.     if (yscale < 1.0) then
  503.     begin
  504.       width := fwidth / yscale;
  505.       fscale := 1.0 / yscale;
  506.       for i := 0 to DstHeight-1 do
  507.       begin
  508.         contrib^[i].n := 0;
  509.         GetMem(contrib^[i].p, trunc(width * 2.0 + 1) * sizeof(TContributor));
  510.         center := i / yscale;
  511.         // Original code:
  512.         // left := ceil(center - width);
  513.         // right := floor(center + width);
  514.         left := floor(center - width);
  515.         right := ceil(center + width);
  516.         for j := left to right do
  517.         begin
  518.           weight := filter((center - j) / fscale) / fscale;
  519.           if (weight = 0.0) then
  520.             continue;
  521.           if (j < 0) then
  522.             n := -j
  523.           else if (j >= SrcHeight) then
  524.             n := SrcHeight - j + SrcHeight - 1
  525.           else
  526.             n := j;
  527.           k := contrib^[i].n;
  528.           contrib^[i].n := contrib^[i].n + 1;
  529.           contrib^[i].p^[k].pixel := n;
  530.           contrib^[i].p^[k].weight := weight;
  531.         end;
  532.       end
  533.     end else
  534.     // Vertical super-sampling
  535.     // Scales from smaller to bigger height
  536.     begin
  537.       for i := 0 to DstHeight-1 do
  538.       begin
  539.         contrib^[i].n := 0;
  540.         GetMem(contrib^[i].p, trunc(fwidth * 2.0 + 1) * sizeof(TContributor));
  541.         center := i / yscale;
  542.         // Original code:
  543.         // left := ceil(center - fwidth);
  544.         // right := floor(center + fwidth);
  545.         left := floor(center - fwidth);
  546.         right := ceil(center + fwidth);
  547.         for j := left to right do
  548.         begin
  549.           weight := filter(center - j);
  550.           if (weight = 0.0) then
  551.             continue;
  552.           if (j < 0) then
  553.             n := -j
  554.           else if (j >= SrcHeight) then
  555.             n := SrcHeight - j + SrcHeight - 1
  556.           else
  557.             n := j;
  558.           k := contrib^[i].n;
  559.           contrib^[i].n := contrib^[i].n + 1;
  560.           contrib^[i].p^[k].pixel := n;
  561.           contrib^[i].p^[k].weight := weight;
  562.         end;
  563.       end;
  564.     end;
  565.  
  566.     // --------------------------------------------------
  567.     // Apply filter to sample vertically from Work to Dst
  568.     // --------------------------------------------------
  569. {$IFDEF USE_SCANLINE}
  570.     SourceLine := Work.ScanLine[0];
  571.     Delta := NativeInt(Work.ScanLine[1]) - NativeInt(SourceLine);
  572.     DestLine := Dst.ScanLine[0];
  573.     DestDelta := NativeInt(Dst.ScanLine[1]) - NativeInt(DestLine);
  574. {$ENDIF}
  575.     for k := 0 to DstWidth-1 do
  576.     begin
  577. {$IFDEF USE_SCANLINE}
  578.       DestPixel := pointer(DestLine);
  579. {$ENDIF}
  580.       for i := 0 to DstHeight-1 do
  581.       begin
  582.         rgb.r := 0;
  583.         rgb.g := 0;
  584.         rgb.b := 0;
  585.         // weight := 0.0;
  586.         for j := 0 to contrib^[i].n-1 do
  587.         begin
  588. {$IFDEF USE_SCANLINE}
  589.           color := PColorRGB(NativeInt(SourceLine)+contrib^[i].p^[j].pixel*Delta)^;
  590. {$ELSE}
  591.           color := Color2RGB(Work.Canvas.Pixels[k, contrib^[i].p^[j].pixel]);
  592. {$ENDIF}
  593.           weight := contrib^[i].p^[j].weight;
  594.           if (weight = 0.0) then
  595.             continue;
  596.           rgb.r := rgb.r + color.r * weight;
  597.           rgb.g := rgb.g + color.g * weight;
  598.           rgb.b := rgb.b + color.b * weight;
  599.         end;
  600.         if (rgb.r > 255.0) then
  601.           color.r := 255
  602.         else if (rgb.r < 0.0) then
  603.           color.r := 0
  604.         else
  605.           color.r := round(rgb.r);
  606.         if (rgb.g > 255.0) then
  607.           color.g := 255
  608.         else if (rgb.g < 0.0) then
  609.           color.g := 0
  610.         else
  611.           color.g := round(rgb.g);
  612.         if (rgb.b > 255.0) then
  613.           color.b := 255
  614.         else if (rgb.b < 0.0) then
  615.           color.b := 0
  616.         else
  617.           color.b := round(rgb.b);
  618. {$IFDEF USE_SCANLINE}
  619.         DestPixel^ := color;
  620.         inc(NativeInt(DestPixel), DestDelta);
  621. {$ELSE}
  622.         Dst.Canvas.Pixels[k, i] := RGB2Color(color);
  623. {$ENDIF}
  624.       end;
  625. {$IFDEF USE_SCANLINE}
  626.       Inc(SourceLine, 1);
  627.       Inc(DestLine, 1);
  628. {$ENDIF}
  629.     end;
  630.  
  631.     // Free the memory allocated for vertical filter weights
  632.     for i := 0 to DstHeight-1 do
  633.       FreeMem(contrib^[i].p);
  634.  
  635.     FreeMem(contrib);
  636.  
  637.   finally
  638.     Work.Free;
  639.   end;
  640. end;
  641.  
  642. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement