Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Круглый стол
  
Правила КС
>> Настройки

Фильтр вопросов
>> Новые вопросы
отслеживать по
>> Новые ответы

Избранное

Страница вопросов
Поиск по КС


Специальные проекты:
>> К л ю к в а
>> Г о л о в о л о м к и

Вопрос №

Задать вопрос
Off-topic вопросы

Помощь

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  02:56[Войти] | [Зарегистрироваться]
Ответ на вопрос № 39689

03-02-2006 02:59
Здравствуйте!!!!!

Спасибо создателям за такой супер сайт!!!!! И соответсвенно людям которые тратят свое драгоценное время на помощь более неопытным прогамистов!!!!
Не один раз вы меня выручали. :))

Даже и не думал что когда нибудь я тоже буду задавать здесь вопрос, но появилась проблема которую не могу решить. Искал и на королевстве и в гугле - не нашел решения.

Поблема вот в чем:
Мне нужно отобразить несколько десятков .jpg фалов на форме в уменьшенном варианте. Чтото наподобие Thumbnails в Explorere. Все нормально при маленких jpg'шках. Но.... у клиента есть файлы более 4мб и их несколько десятков. При загрузки ОДНОГО 4мб jpg в память программа раздувается до 60мб!!!!!!!!! Что непримленно много. И рисуется такой файл на форме где-то 4 секунды!!!! А в Windows Explorer все 30 файлов рисуются за секунды. Как мне быть???

Я использую стандартные средства (TJPEGImage, TImage.StretchDraw). Пробовал FastFiles, FastDIB но там нужна какая-то библиотека "ijl15.dll". Подумал использовать DirectX но не знаю если поможет.

Может кто нибудь сталкивался с такой проблемой если нет хотябы подскажите в каком направлении искать. Буду очень благодарен!!!!

[+] Добавить в избранные вопросы

Отслеживать ответы на этот вопрос по RSS

Ответы:


Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице.
Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.

11-05-2007 00:23 | Комментарий к предыдущим ответам
DIBNeeded действительно всё ускоряет, но сам по себе медленный, как бы его ускорить вот его код

procedure TJPEGImage.DIBNeeded;
begin
  GetBitmap;
end;

function TJPEGImage.GetBitmap: TBitmap;
var
  LinesPerCall, LinesRead: Integer;
{$IFDEF JPEGSO}
  DestScanLine, CurPixel: PChar;
  Swap: Char;
  AWidth: Integer;
{$ELSE}
  DestScanLine: PChar;
{$ENDIF}
  PtrInc: Integer;
  jc: TJPEGContext;
  GeneratePalette: Boolean;
begin
  Result := FBitmap;
  if Result <> nil then Exit;
  if (FBitmap = nil) then FBitmap := TBitmap.Create;
  Result := FBitmap;
  GeneratePalette := True;

  InitDecompressor(Self, jc);
  try
    try
      // Set the bitmap pixel format
      FBitmap.Handle := 0;
      if (PixelFormat = jf8Bit) or (jc.d.out_color_space = JCS_GRAYSCALE) then
        FBitmap.PixelFormat := pf8bit
      else
        FBitmap.PixelFormat := pf24bit;

      Progress(Self, psStarting, 0, False, Rect(0,0,0,0), '');
      try
        if (FTempPal <> 0) then
        begin
          if (FPixelFormat = jf8Bit) then
          begin                        // Generate DIB using assigned palette
            BuildColorMap(jc.d, FTempPal);
            FBitmap.Palette := CopyPalette(FTempPal);  // Keep FTempPal around
            GeneratePalette := False;
          end
          else
          begin
            DeleteObject(FTempPal);
            FTempPal := 0;
          end;
        end;

        jpeg_start_decompress(jc.d);

        // Set bitmap width and height
        with FBitmap do
        begin
          Handle := 0;
          Width := jc.d.output_width;
          Height := jc.d.output_height;
          DestScanline := ScanLine[0];
          PtrInc := Integer(ScanLine[1]) - Integer(DestScanline);
          if (PtrInc > 0) and ((PtrInc and 3) = 0) then
            // if no dword padding is required and output bitmap is top-down
            LinesPerCall := jc.d.rec_outbuf_height // read multiple rows per call
          else
            LinesPerCall := 1;            // otherwise read one row at a time
        end;

        if jc.d.buffered_image then
        begin  // decode progressive scans at low quality, high speed
          while jpeg_consume_input(jc.d) <> JPEG_REACHED_EOI do
          begin
            jpeg_start_output(jc.d, jc.d.input_scan_number);
            // extract color palette
            if (jc.common.progress^.completed_passes = 0) and (jc.d.colormap <> nil)
              and (FBitmap.PixelFormat = pf8bit) and GeneratePalette then
            begin
              FBitmap.Palette := BuildPalette(jc.d);
              PaletteModified := True;
            end;
            DestScanLine := FBitmap.ScanLine[0];
            while (jc.d.output_scanline < jc.d.output_height) do
            begin
              LinesRead := jpeg_read_scanlines(jc.d, @DestScanline, LinesPerCall);
{$IFDEF JPEGSO}
              if PixelFormat = jf24bit then
              begin
                CurPixel := DestScanLine;
                AWidth := FBitmap.Width;
                while (CurPixel - DestScanLine) < (AWidth * 3) do
                begin
                  Swap := CurPixel[0];
                  CurPixel[0] := CurPixel[2];
                  CurPixel[2] := Swap;
                  Inc(CurPixel, 3);
                end;
              end;
{$ENDIF}
              Inc(Integer(DestScanline), PtrInc * LinesRead);
            end;
            jpeg_finish_output(jc.d);
          end;
          // reset options for final pass at requested quality
          jc.d.dct_method := jc.FinalDCT;
          jc.d.dither_mode := jc.FinalDitherMode;
          if jc.FinalTwoPassQuant then
          begin
            jc.d.two_pass_quantize := True;
            jc.d.colormap := nil;
          end;
          jpeg_start_output(jc.d, jc.d.input_scan_number);
          DestScanLine := FBitmap.ScanLine[0];
        end;

        // build final color palette
        if (not jc.d.buffered_image or jc.FinalTwoPassQuant) and
          (jc.d.colormap <> nil) and GeneratePalette then
        begin
          FBitmap.Palette := BuildPalette(jc.d);
          PaletteModified := True;
          DestScanLine := FBitmap.ScanLine[0];
        end;
        // final image pass for progressive, first and only pass for baseline
        while (jc.d.output_scanline < jc.d.output_height) do
        begin
          LinesRead := jpeg_read_scanlines(jc.d, @DestScanline, LinesPerCall);
{$IFDEF JPEGSO}
          if PixelFormat = jf24bit then
          begin
            CurPixel := DestScanLine;
            AWidth := FBitmap.Width;
            while (CurPixel - DestScanLine) < (AWidth * 3) do
            begin
              Swap := CurPixel[0];
              CurPixel[0] := CurPixel[2];
              CurPixel[2] := Swap;
              Inc(CurPixel, 3);
            end;
          end;
{$ENDIF}         
          Inc(Integer(DestScanline), PtrInc * LinesRead);
        end;

        if jc.d.buffered_image then jpeg_finish_output(jc.d);
        jpeg_finish_decompress(jc.d);
      finally
        if ExceptObject = nil then
          PtrInc := 100
        else
          PtrInc := 0;
        Progress(Self, psEnding, PtrInc, PaletteModified, Rect(0,0,0,0), '');
        // Make sure new palette gets realized, in case OnProgress event didn't.
        if PaletteModified then
          Changed(Self);
      end;
    except
      on EAbort do// OnProgress can raise EAbort to cancel image load
    end;
  finally
    ReleaseContext(jc);
  end;
end;


13-09-2006 10:48 | Комментарий к предыдущим ответам
Здравствуйте,
А как все-таки добиться ещё большей скорости, вот например как в Picasa?

29-08-2006 05:31 | Комментарий к предыдущим ответам
Здравствуйте,
Очень интересно было найти именно то, над чем в данный момент работаю: делаю компонент для thumbnail.
Хотел поделится кодом, может у кого будут замечания или критика :-)
Загружал Thumbnail разными путями:
- с exif (dExif)
- с помощью Gr32 (Graphicd32 lib)
- нашёл в этом форуме ответ Python'а - намного быстрее.
- с потоком также пробовал - добавил в компонент, но столкнулся с такой проблемой: когда на форме 50 компонентов для изображения thumbnails, тогда используется много памяти (600 Мб). Решил убрать поток с компонента и добавит в компонент parent - контейнер, содержащий все preview.

Буду благодарен за Ваши замечания.
С уважением,
Олег

код:



{
Source Name: PreviewItem
Description: component to display preview of an image
Copyright (C) Oleh Lozynskyy <oleh.lozynskyy...at... ask me>

26/08/2006 - study
27/08/2006 - code (properties, colors, drawing)
28/08/2006 - code (threads added)
29/08/2006 - code (removed threads)

}


unit uPreviewItem;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls,

  Math,

  jpeg,

  gr32,
  GR32_Transforms

;

type
  TFX_GetBmpPreview = function(AFilename: PChar; Var ABmp: TBitmap): Integer;

  TPreviewItem = class (TCustomPanel)
  private
    FFilename: String;
    FSelected: Boolean;
    FActive: Boolean;
    FSize: Integer;
    FBorderWidth: Byte;
   
    FBmpImage: TBitmap;
    FOriBmpImage: TBitmap; // original copy in case FBmpImage was modified

    // colors
    FSelectedColor: TColor;
    FActiveColor: TColor; // not selected
    FInActiveColor: TColor;
    FInActiveBlendColor: TColor;

    FDllHandle: THandle;
    Ffx_GetBmpPreview: TFX_GetBmpPreview;

    // events
    FOnItemSelect: TNotifyEvent;
    FOnMouseLeave: TNotifyEvent;
    FOnMouseEnter: TNotifyEvent;

    FRefreshByParent: Boolean;

    procedure CountOnMaxXSize(h1,w1,side: integer; Var h2: Integer; var w2: integer);

    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;

    // properties setters
    procedure SetFilename(AValue: String);
    procedure SetSize(AValue: Integer);
    procedure SetActive(AValue: Boolean);
    procedure SetSelected(AValue: Boolean);

    procedure SetSelectedColor(AValue: TColor);
    procedure SetActiveColor(AValue: TColor);
    procedure SetInActiveColor(AValue: TColor);
    procedure SetInActiveBlendColor(AValue: TColor);

    // Event response
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure DbLCLick; override;

    // component code
    procedure ReadPreview;
  protected
    FMouseOver: Boolean;
    function Lighter(AValue: TColor; APercent: Byte): TColor;
    function Darker(AValue: TColor; APercent: Byte): TColor;
    procedure BlendBmp(source: TBitmap; Dest: TBitmap; color: TColor);

    function GeneratePreview(AFilename: String; AThumbWidth: Integer;
            var aBmp: TBitmap): Integer;

    //function GetPreviewFromExif(AFilename: String; AThumbWidth: Integer;
    //        var aBmp: TBitmap): Integer;

    function GetPreviewFromJpeg(AFilename: String; AThumbWidth: Integer;
            var aBmp: TBitmap): Integer;

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
    procedure Clear;
    procedure CleanPreview;

    procedure SetFilenameAndPreview(AFilename: String; ABmp: TBitmap);

  published
    {inherited}
    property Align;
    property Alignment;
    property Anchors;
    property Enabled;
    property PopupMenu;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property OnCanResize;
    property OnClick;
    property OnConstrainedResize;
    property OnContextPopup;
    property OnDockDrop;
    property OnDockOver;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGetSiteInfo;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;

    property Filename: String read FFilename write SetFilename;
    property Size: Integer read FSize write SetSize;
    property Selected: Boolean read FSelected write SetSelected;
    property Active: Boolean read FActive write SetActive;
    property Image: TBitmap read FBmpImage;
    property OriginalImage: TBitmap read FOriBmpImage;
    property SelectedColor: TColor read FSelectedColor write SetSelectedColor;
    property ActiveColor: TColor read FActiveColor write SetActiveColor;
    property InActiveColor: TColor read FInActiveColor write SetInActiveColor;
    property InActiveBlendColor: TColor read FInActiveBlendColor write SetInActiveBlendColor;


    property OnItemSelect: TNotifyEvent read FOnItemSelect write FOnItemSelect;
    property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
    property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;

  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('LOM', [TPreviewItem]);
end;

{ TPreviewItem }

constructor TPreviewItem.Create;
begin
  inherited;
  // not recommended to change
  FMouseOver := False;
  FActive    := True;
  FSelected  := False;
  FRefreshByParent := True;

  // these values could be changed
  FSize  := 120;
 
  Width  := 120;
  Height := 120;

  FBorderWidth := 4;

  // recommended to change :-)
  FSelectedColor      := clBlue;
  FActiveColor        := clWhite; // not selected
  FInActiveColor      := clGray;
  FInActiveBlendColor := $40200000;
end;

destructor TPreviewItem.Destroy;
begin
  inherited;
  if Assigned(FBmpImage)    then FBmpImage.Free;
  if Assigned(FOriBmpImage) then FOriBmpImage.Free;
end;

procedure TPreviewItem.Paint;
var
  memoryBitmap: TBitmap;
  //baseX, baseY: word;
  //useColor: TColor;
begin
  if not Visible then exit;
  // inherited;

  try
    memoryBitmap := TBitmap.Create;
    try
      memoryBitmap.Width := ClientRect.Right;
      memoryBitmap.Height := ClientRect.Bottom;
     
      try
        // Initialise memory bitmap

        SetBkMode(MemoryBitmap.Canvas.Handle, windows.TRANSPARENT);
        if FMouseOver then
          if FActive then
            memoryBitmap.Canvas.Brush.Color := Darker(FActiveColor, 10)
          else memoryBitmap.Canvas.Brush.Color := Lighter(FInActiveColor, 10)
        else if FActive then memoryBitmap.Canvas.Brush.Color := FActiveColor
            else memoryBitmap.Canvas.Brush.Color := FInActiveColor;

        memoryBitmap.PixelFormat := pf32bit;
        memoryBitmap.Canvas.FillRect(ClientRect);
      except
        ShowMessage('in the begining');
      end;



      try
        if FSelected then begin
          memoryBitmap.Canvas.Pen.Width := FBorderWidth;
          if FMouseOver then
            if FActive then
              memoryBitmap.Canvas.pen.Color := Lighter(FSelectedColor, 40)
            else memoryBitmap.Canvas.pen.Color := Darker(FInActiveColor, 10)
          else if FActive then
                memoryBitmap.Canvas.pen.Color := FSelectedColor
              else memoryBitmap.Canvas.pen.Color := FInActiveColor;

          //memoryBitmap.Canvas.Brush.Color := clWhite;
          memoryBitmap.Canvas.Rectangle(ClientRect.Left, ClientRect.Top,
          ClientRect.Right, ClientRect.Bottom);
        end;



      // Draw Border
      {if not( FBorderStyle = tbsNone ) then begin
        if not(FSelected) then begin
          memoryBitmap.Canvas.pen.Color := FBorderColor;
          memoryBitmap.Canvas.Brush.Color := FColor;
        end
        else begin
          memoryBitmap.Canvas.pen.Color := FSelectedBorderColor;
          memoryBitmap.Canvas.Brush.Color := FSelectedColor;
        end;

        if FBorderStyle = tbsRoundedRect then begin
          memoryBitmap.Canvas.RoundRect(ClientRect.Left, ClientRect.Top,
            ClientRect.Right, ClientRect.Bottom, 14, 14);
        end
        else begin
          memoryBitmap.Canvas.Rectangle(ClientRect.Left, ClientRect.Top,
            ClientRect.Right, ClientRect.Bottom);
        end;
      end; }

      except
        ShowMessage('error drawing border');
      end;

      try
        // thumbnail
        if Assigned(FOriBmpImage) then begin
          if not(FActive) then begin
            BlendBmp(FOriBmpImage, FBmpImage, lighter(FInActiveBlendColor,50));
            //FBmpImage.Assign(FOriBmpImage); // but grey or inactive
          end
          else FBmpImage.Assign(FOriBmpImage);


          memoryBitmap.Canvas.StretchDraw(
            Rect( ClientRect.Left + FBorderWidth,ClientRect.top + FBorderWidth,
                  ClientRect.Right - FBorderWidth, ClientRect.Bottom - FBorderWidth),
            FBmpImage);
        end;
      except
        ShowMessage('thumbnail draw failed');
      end;

      // Copy memoryBitmap to screen
      try
        canvas.CopyRect(ClientRect, memoryBitmap.canvas, ClientRect);
      except
        ShowMessage('bitmap is not copied to canvas');
      end;
    finally
      if assigned(memorybitmap) then
        memoryBitmap.Free;
    end;
  except
    ShowMessage('generic error');
  end;
end;

procedure TPreviewItem.ReadPreview;
var
  iRez: Integer;
  aFile: PChar;
  h,w: Integer;
begin
  // if filename doesn't exists - leave
  if not FileExists(FFilename) then exit;

  // implement DLL to get thumbnails - done

  if not Assigned(FOriBmpImage) then
    FOriBmpImage := TBitmap.Create;
  if not Assigned(FBmpImage) then
    FBmpImage := TBitmap.Create;

   
  // load DLL
  FDllHandle := LoadLibrary('ppPreview.dll');
  if FDllHandle <> 0 then
  begin
    try
      @Ffx_GetBmpPreview := GetProcAddress(FDllHandle, 'GetBmpPreviewFromFile');
      // Call function
      GetMem(aFile,Length(FFilename) + 1);
      StrCopy(aFile, PChar(FFilename));
      // get Thumbnail from Dll
      iRez := Ffx_GetBmpPreview(aFile, FOriBmpImage);
      // free mem
      FreeMem(aFile);

    except
      // if some problems with dll occured
      GeneratePreview(FFilename, 120, FOriBmpImage);
    end;
      try
        FreeLibrary(FDllHandle);
      finally
        FDllHandle := 0;
      end;

  end
  else begin
    // create preview in default way
    // GeneratePreview(FFilename, 120, FOriBmpImage);

    //if (GetPreviewFromExif(FFilename, 120, FOriBmpImage) > 0) then begin
      GetPreviewFromJpeg(FFilename, 120, FOriBmpImage);
    //  FSelected := True;
    //end;
    //ShowMessage('DLL not found');
  end;
  // self.Height := FOriBmpImage.Height + FBorderWidth*2 + 2;
  // self.Width  := FOriBmpImage.Width + FBorderWidth*2 + 2;

  CountOnMaxXSize(FOriBmpImage.Height + FBorderWidth*2 + 2,
      FOriBmpImage.Width + FBorderWidth*2 + 2,
      FSize,
      h,
      w);
  self.Height := h;
  self.Width  := w;

 
  FBmpImage.Assign( FOriBmpImage );
end;


procedure TPreviewItem.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  if Assigned(FOnMouseEnter) then
    FOnMouseEnter(Self);
  FMouseOver := True;
  Invalidate;
end;

procedure TPreviewItem.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  if Assigned(FOnMouseLeave) then
    FOnMouseLeave(Self);
  FMouseOver := False;
  Invalidate;
end;

function TPreviewItem.Lighter(AValue: TColor; APercent: Byte): TColor;
var
  rgbColor: TColor;
  r, g, b: Byte;
  r2, g2, b2: Byte;
begin
  rgbColor := ColorToRGB(AValue);
  r := GetRValue(rgbColor);
  g := GetGValue(rgbColor);
  b := GetBValue(rgbColor);
  r2 := r + muldiv(255 - r, APercent, 100);
  if r2 < r then
    r2 := 255;
  g2 := g + muldiv(255 - g, APercent, 100);
  if g2 < g then
    g2 := 255;
  b2 := b + muldiv(255 - b, APercent, 100);
  if b2 < b then
    b2 := 255;
  result := RGB(r2, g2, b2);
end;


function TPreviewItem.Darker(AValue: TColor; APercent: Byte): TColor;
var
  rgbColor: TColor;
  r, g, b: Byte;
  r2, g2, b2: Byte;
begin
  rgbColor := ColorToRGB(AValue);
  r := GetRValue(rgbColor);
  g := GetGValue(rgbColor);
  b := GetBValue(rgbColor);
  r2 := r - muldiv(r, APercent, 128);
  if r2 > r then
    r2 := 0;
  g2 := g - muldiv(g, APercent, 128);
  if g2 > g then
    g2 := 0;
  b2 := b - muldiv(b, APercent, 128);
  if b2 > b then
    b2 := 0;
  result := RGB(r2, g2, b2);
end;



procedure TPreviewItem.DbLCLick;
begin
  inherited;
  // todo for DoubleClick
end;


procedure TPreviewItem.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited;

  if Button = mbLeft then begin
    // If Left btn pressed
    if assigned(FOnItemSelect) then
      FOnItemSelect(Self);
  end
  else if Button = mbRight then begin
    if assigned(FOnItemSelect) then
      FOnItemSelect(Self);
      //BuildMenu(Mouse.CursorPos.X, Mouse.CursorPos.Y);
  end;

end;



procedure TPreviewItem.SetActive(AValue: Boolean);
begin
  if (FActive <> AValue) then begin
    FActive := AValue;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetActiveColor(AValue: TColor);
begin
  if (FActiveColor <> AValue) then
  begin
    FActiveColor := AValue;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetFilename(AValue: String);
begin
  if (FFilename <> AValue) then
  begin
    FFilename := AValue;
    ReadPreview;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetInActiveColor(AValue: TColor);
begin
  if (FInActiveColor <> AValue) then
  begin
    FInActiveColor := AValue;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetInActiveBlendColor(AValue: TColor);
begin
  if (FInActiveBlendColor <> AValue) then
  begin
    FInActiveBlendColor := AValue;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetSize(AValue: Integer);
begin
  if (FSize <> AValue) then begin
    FSize := AValue;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetSelected(AValue: Boolean);
begin
  if (FSelected <> AValue) then begin
    FSelected := AValue;
    Invalidate;
  end;
end;

procedure TPreviewItem.SetSelectedColor(AValue: TColor);
begin
  if (FSelectedColor <> AValue) then
  begin
    FSelectedColor := AValue;
    Invalidate;
  end;
end;


procedure TPreviewItem.BlendBmp(source, Dest: TBitmap; color: TColor);
var
  P: PColor32;
  X, Y, res: integer;
  red, alpha: byte;
  c: TColor;
  dst32, src32: TBitmap32;
begin
  dst32 := TBitmap32.Create;
  src32 := TBitmap32.Create;
  src32.Assign(source);

  //Dest.assign(source);
  dst32.SetSize(src32.Width, src32.Height);
  src32.DrawTo(dst32);
  dst32.Font.Name := src32.Font.Name;
  dst32.Font.Size := src32.Font.Size;
  dst32.Font.Style := src32.Font.Style;
  dst32.Font.Color := src32.Font.Color;
  dst32.DrawMode := src32.DrawMode;
  dst32.CombineMode := src32.CombineMode;
  with dst32 do
  begin
    P := PixelPtr[0, 0];
    for Y := 0 to Height - 1 do
    begin
      for X := 0 to Width - 1 do
      begin
        alpha := (P^ shr 24);
        red := (P^ and $00FF0000) shr 16;
        res := red - 178;// 128;
        if (res < 0) then
          c := Darker(color, -res)
        else
          if (res > 0) then
            c := Lighter(color, res)
          else
            c := color;
        P^ := color32(GetRValue(c), GetGValue(c), GetBValue(c), alpha);
        inc(P); // proceed to the next pixel
      end;
    end;
  end;
  Dest.Assign(dst32);
  dst32.Free;
  src32.Free;
end;

  // helper function to get thumbnails using Graphics32 lib
function TPreviewItem.GeneratePreview(AFilename: String;
    AThumbWidth: Integer; var aBmp: TBitmap): Integer;
var
  bmpSrc, bmpDst: TBitmap32;
  AT: TAffineTransformation;
  sScale: Single;
begin
  //ShowMessage('Loading preview with Gr32.');
  try
    bmpSrc := TBitmap32.Create();
    bmpDst := TBitmap32.Create();
    AT := TAffineTransformation.Create;

    bmpSrc.LoadFromFile(AFilename);
    // ver 1.5 - bmpSrc.StretchFilter := sfLanczos;//, sfLinear, sfSpline, sfLanczos, sfMitchell
    bmpSrc.ResamplerClassName := 'TDraftResampler';

    if bmpSrc.Width > bmpSrc.Height then begin
      sScale := AThumbWidth / bmpSrc.Height;
      bmpDst.Height := AThumbWidth;
      bmpDst.Width :=  round(bmpSrc.Width * AThumbWidth / bmpSrc.Height);
    end
    else begin
      sScale := AThumbWidth / bmpSrc.Width;
      bmpDst.Width := AThumbWidth;
      bmpDst.Height :=  round(bmpSrc.Height * AThumbWidth / bmpSrc.Width);
    end;

    AT.SrcRect := FloatRect(0, 0, bmpSrc.Width - 1, bmpSrc.Height - 1);
    AT.Clear;
    AT.Scale(sScale, sScale);
    Transform(bmpDst, bmpSrc, AT);


    aBmp.Assign(bmpDst);

    FreeAndNil(bmpSrc);
    FreeAndNil(bmpDst);
    AT.Free;
  except
    result := 1;
  end;
  Result := 0;
end;

procedure TPreviewItem.Clear;
begin
  FBmpImage.Free;
  FOriBmpImage.Free;
  FBmpImage := nil;
  FOriBmpImage :=  nil;
  FFilename := '';
end;

procedure TPreviewItem.SetFilenameAndPreview(AFilename: String;
  ABmp: TBitmap);
begin
  FFilename    := AFilename;
  FOriBmpImage := ABmp;
  FBmpImage    := ABmp;
  Invalidate;
end;

procedure TPreviewItem.CountOnMaxXSize(h1, w1, side: integer; var h2,
  w2: integer);
begin
  if (h1 > w1) then begin
    h2 := side;
    w2 := ceil( min (h1,w1) * side / max (h1,w1) );
  end
  else begin
    h2 := ceil (min (h1,w1) * side / max (h1,w1) );
    w2 := side;
  end;
end;

procedure TPreviewItem.CleanPreview;
begin

  with Self do
  begin
    Clear;
    Height := FSize;
    Width  := FSize;
    FSelected := False;
  end;

end;

function TPreviewItem.GetPreviewFromJpeg(AFilename: String;
  AThumbWidth: Integer; var aBmp: TBitmap): Integer;
var
  jpg: TJpegImage;
  Rect: TRect;
begin
  // code by Python
  // from: http://www.delphikingdom.com/asp/answer.asp?IDAnswer=39689
  jpg := TJpegImage.Create;
  try
    jpg.Smoothing  := False;
    jpg.Performance := jpBestSpeed;

    jpg.LoadFromFile(AFilename);

    if (jpg.Width > jpg.Height) then
      if (jpg.Width >= AThumbWidth div 4) then jpg.Scale := jsHalf
      else if (jpg.Width >= AThumbWidth div 2) then jpg.Scale := jsQuarter
          else if (jpg.Width >= AThumbWidth) then jpg.Scale := jsEighth
                else jpg.Scale := jsFullSize
  else if jpg.Height>=AThumbWidth div 4 then jpg.Scale := jsHalf
      else if jpg.Height>=AThumbWidth div 2 then jpg.Scale := jsQuarter
            else if jpg.Height >= AThumbWidth then jpg.Scale := jsEighth
                else jpg.Scale := jsFullSize;

  if (jpg.Width/AThumbWidth > jpg.Height / AThumbWidth)then begin
    aBmp.Width  := AThumbWidth;
    aBmp.Height := round(jpg.Height/jpg.Width*AThumbWidth);
  end
  else begin
    aBmp.Height := AThumbWidth;
    aBmp.Width  := round(jpg.Width/jpg.Height*AThumbWidth);
  end;
    Rect.Left  := 0;
    Rect.Top    := 0;
    Rect.Right  := aBmp.Width;
    Rect.Bottom := aBmp.Height;
    aBmp.Canvas.StretchDraw(Rect,jpg);
  finally
    jpg.Free;
  end;
end;

{
using dEXIF - Copyright 2001-2004, Gerry McGuire

function TPreviewItem.GetPreviewFromExif(AFilename: String;
  AThumbWidth: Integer; var aBmp: TBitmap): Integer;
var
  jpegThumb: TJpegImage;
  noThumbnailInline: Boolean;
  ImgData: TimgData;
begin
  // using dEXIF - Copyright 2001-2004, Gerry McGuire
    try
      // try to get preview from Exif inline
                      noThumbnailInline := True;
      ImgData := TimgData.Create;
                      try
                        ImgData.BuildList := GenAll;  // on by default anyway
                        ImgData.ProcessFile(AFilename);
                        if ImgData.HasThumbnail then
                        begin
                          ImgData.ExifObj.ProcessThumbnail;
                          try
                                    // save it
                                    jpegThumb := imgData.ExtractThumbnailJpeg();

            // Bmp needed
            aBmp.Height := jpegThumb.Height;
            aBmp.Width  := jpegThumb.Width;
            aBmp.PixelFormat := pf24bit;

            //jpegThumb.SaveToFile('C:\002.jpg');

            jpegThumb.DIBNeeded;
            aBmp.Assign(jpegThumb);
            // aBmp.SaveToFile('C:\002.bmp');
                                     jpegThumb.Free;
                                    noThumbnailInline := False;
                          except
                                    noThumbnailInline := True;
                          end;
                        end;
                      finally
                        ImgData.Free;
                      end;
    except
      Result := 2
    end;
    if (noThumbnailInline) then
      Result := 0
    else Result := 1;
end;
}


end.


30-03-2006 03:55 | Сообщение от автора вопроса
to Python:

Спасибо за код но я уже разобрался с потоками.
Я создал отдельный поток который для начала рисует стандартную иконку для каждого файла потом по мере загрузки заменяет иконку на thumbnail. Это очень удобно.
У меня он кеширует thumbnail'ы, релизован останов потока когда пользователь закрывает форму или нажимает на reload итд.

То что мне удалось разузнать:
- можно вытаскивать встроенные thumbnail'ы которые цифровые фотокамеры записывают в EXIF заголовке JPEG файла.
  http://mcguirez.homestead.com/downloads.html

- можно загрузить JPEG с ProgressiveDisplay := True. Так можно прочитать только часть файла и нарисовать его. Получится та же изображение только с низким разрешением. Проблема в том что ProgressiveEncode уже мало где используется.

- испробовал даже Pegasus Imaging библиотеку для работы с JPEG, её ACDSee использует, и заметил лишь очень малый прирост в производительности :(

Спасибо всем кто помог мне.

К слову, подумываю чтобы написать статью об использовании потоков для начинающих.

26-03-2006 12:38
>>> после загрузки 30 jpeg'ов программа раздувается до 500мб
Не знаю, вот реализовал что-то похожее. Работает, конечно, не быстро, но приемлимо. На 2000 (чуть поменьше) файлов суммарным размером 90 Мб тратит 8-10 секунд (в зависимости видимо от нагрузки процессора, действий пользователя и фазы луны). Но во время загрузки пользователь уже может выполнять просмотр уже загруженных картинок. Конечно, алгоритм сырой (если пользователь переключился на какую-то картинку, то надо раздобыть сначала ее, Thumbnail не кешируются ну и прочие недоработки). Но за базу для тебя (да и других посетитителей этого вопроса) вполне сойдет.
DFM:

object Loader: TLoader
  Left = 192
  Top = 116
  BorderStyle = bsDialog
  Caption = 'Картиночник'
  ClientHeight = 203
  ClientWidth = 308
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 8
    Width = 89
    Height = 13
    Caption = 'Откуда картинки:'
  end
  object Prev: TImage
    Left = 96
    Top = 36
    Width = 105
    Height = 105
  end
  object Path: TEdit
    Left = 104
    Top = 4
    Width = 169
    Height = 21
    ReadOnly = True
    TabOrder = 0
  end
  object Br: TButton
    Left = 280
    Top = 4
    Width = 21
    Height = 21
    Caption = '...'
    TabOrder = 1
    OnClick = BrClick
  end
  object Pred: TButton
    Left = 64
    Top = 152
    Width = 75
    Height = 25
    Caption = '< Назад'
    Enabled = False
    TabOrder = 2
    OnClick = PredClick
  end
  object Next: TButton
    Left = 160
    Top = 152
    Width = 75
    Height = 25
    Caption = 'Вперед >'
    Enabled = False
    TabOrder = 3
    OnClick = PredClick
  end
  object Prog: TProgressBar
    Left = 0
    Top = 186
    Width = 308
    Height = 17
    Align = alBottom
    Min = 0
    Max = 100
    Smooth = True
    TabOrder = 4
  end
end


PAS:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ComCtrls;

const WM_STATUS=WM_USER+102;

type PBArray=^TBArray;
    TBArray=array of TBitmap;

type TLoadThread=class(TThread)
      Constructor Create(aHandle:cardinal;aPath:string;aPreview:PBArray;aWidth,aHeight:integer);
      Procedure Execute;override;
      private
      Path:string;
      pWidth,pHeight:integer;
      Preview:PBArray;
      ParentHandle:cardinal;
    end;

type
  TLoader = class(TForm)
    Label1: TLabel;
    Path: TEdit;
    Br: TButton;
    Prev: TImage;
    Pred: TButton;
    Next: TButton;
    Prog: TProgressBar;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BrClick(Sender: TObject);
    procedure PredClick(Sender: TObject);
  private
    Cur,CurMax:integer;
    Preview:TBArray;
    Time:cardinal;
    LoadThread:TLoadThread;
  protected
    procedure GetStatus(var M:TMessage);message WM_STATUS;
  end;

var
  Loader: TLoader;

implementation

{$R *.dfm}

Uses FileCtrl,Jpeg;

procedure TLoader.FormCreate(Sender: TObject);
begin
SetLength(Preview,0);Cur:=0;LoadThread:=nil;
Pred.Enabled:=false;Next.Enabled:=false;
end;

procedure TLoader.FormDestroy(Sender: TObject);
var I:integer;
begin
For I:=0 to High(Preview) do Preview[I].Free;
SetLength(Preview,0);
Pred.Enabled:=false;Next.Enabled:=false;
FreeAndNil(LoadThread);
end;

procedure TLoader.BrClick(Sender: TObject);
var S:string;
begin
Br.Enabled:=false;
S:=Path.Text;
if SelectDirectory('Выберите каталог:','',S) then begin
  FreeAndNil(LoadThread);
  Path.Text:=S;FormDestroy(Sender);CurMax:=0;Cur:=0;
  Prog.Position:=0;
  Caption:='Загрузка...';
  Time:=GetTickCount;
  LoadThread:=TLoadThread.Create(Handle,S,@Preview,Prev.Width,Prev.Height);
end;
Br.Enabled:=true;
end;

procedure TLoader.PredClick(Sender: TObject);
begin
if(Sender=Pred)and(Cur>0)then Dec(Cur);
if(Sender=Next)and(Cur<CurMax)then Inc(Cur);
Prev.Canvas.Brush.Color:=Color;
Prev.Canvas.FillRect(Prev.ClientRect);
Prev.Canvas.Draw((Prev.Width-Preview[Cur].Width)div 2,
  (Prev.Height-Preview[Cur].Height)div 2,Preview[Cur]);
Pred.Enabled:=Cur>0;
Next.Enabled:=Cur<CurMax;
Prog.Position:=Cur;
end;

procedure TLoader.GetStatus(var M: TMessage);
begin
if M.WParam=0 then Prog.Max:=M.LParam;
Prog.Position:=M.WParam;
CurMax:=M.WParam;
if M.WParam=1 then PredClick(nil);
if M.WParam=M.LParam then begin
  Caption:=Format('На загрузку ушло %4.2f секунды',[(GetTickCount-Time)/1000]);
  PredClick(nil);
  LoadThread:=nil;
end;
end;

{ TLoadThread }

constructor TLoadThread.Create(aHandle:cardinal;aPath: string; aPreview:PBArray;aWidth,aHeight:integer);
begin
inherited Create(true);
SetCurrentDir(aPath);
Path:=IncludeTrailingPathDelimiter(aPath)+'*.jpg';
FreeOnTerminate:=true;
pWidth:=aWidth;
pHeight:=aHeight;
Preview:=aPreview;
ParentHandle:=aHandle;
Resume;
end;

procedure TLoadThread.Execute;
var Ji:TJpegImage;
    Rct:TRect;
    Sr:TSearchRec;
    Cnt,Max:cardinal;
begin
Max:=0;
if FindFirst(Path,faAnyFile,Sr)=0 then repeat
  Inc(Max);
until FindNext(Sr)<>0;
FindClose(Sr);
SetLength(Preview^,Max);Dec(Max);
For Cnt:=0 to Max do Preview^[Cnt]:=nil;
Ji:=TJpegImage.Create;
Ji.Smoothing:=false;
Ji.Performance:=jpBestSpeed;
Rct.Left:=0;Rct.Top:=0;Cnt:=0;
if FindFirst(Path,faAnyFile,Sr)=0 then try
  repeat
  Ji.LoadFromFile(Sr.Name);
  if Ji.Width>Ji.Height then
    if Ji.Width>=pWidth then Ji.Scale:=jsEighth
    else if Ji.Width>=pWidth div 2 then Ji.Scale:=jsQuarter
    else if Ji.Width>=pWidth div 4 then Ji.Scale:=jsHalf
    else Ji.Scale:=jsFullSize
  else
    if Ji.Height>=pHeight then Ji.Scale:=jsEighth
    else if Ji.Height>=pHeight div 2 then Ji.Scale:=jsQuarter
    else if Ji.Height>=pHeight div 4 then Ji.Scale:=jsHalf
    else Ji.Scale:=jsFullSize;
  Preview^[Cnt]:=TBitmap.Create;
  if(Ji.Width/pWidth>Ji.Height/pHeight)then begin
    Preview^[Cnt].Width:=pWidth;
    Preview^[Cnt].Height:=round(Ji.Height/Ji.Width*pHeight);
  end else begin
    Preview^[Cnt].Height:=pHeight;
    Preview^[Cnt].Width:=round(Ji.Width/Ji.Height*pWidth);
  end;
  Rct.Right:=Preview^[Cnt].Width;
  Rct.Bottom:=Preview^[Cnt].Height;
  Preview^[Cnt].Canvas.StretchDraw(Rct,Ji);
  PostMessage(ParentHandle,WM_STATUS,Cnt,Max);
  Inc(Cnt);
  until FindNext(Sr)<>0;
finally
  FindClose(Sr);
  Ji.Free;
end;
end;

end.


Посмотри, может поможет.

08-02-2006 02:07 | Сообщение от автора вопроса
to DRON:

После загрузки JPEGа я ставлял Performance=jpBestSpeed, Smoothing=False и Scale=jsEighth. Потом рисовал на форме обычным StretchDraw. Просто я тестировал с большими JPEGами (8Mb) и даже с Scale=jsEighth они занимали очень много памяти.

Сейчас же перед рисованием использую DIBNeeded (скорость рисования повысилась на 20%) рисую с помощью StretchDraw на Bitmap в памяти (уменьшаю изображение)а потом обычным Draw рисую этот Bitmap на форме. Тем самым я сохраняю не оригинальный JPEG а только уменьшенный вариант в Bitmap формате.
Это я сделал после того как отправил последнее сообщение.

Если я делаю чтото не так подскажи. В общем я доволен как работает алгоритм только по сравнению с Windows он работает в 30 раз медленней. :((

07-02-2006 09:19
- как сделать чтобы картинки занимали меньше места в памяти (сейчс после загрузки 30 jpeg'ов программа раздувается до 500мб :)))
Так вы что картинки не уменьшаете что ли? Прямо через CopyRect на форме рисуете (ну или Stretch у Image стоит)? Если так, то неудивительно, что у вас низкая скорость и даже IJL не помогает. Надо уменьшить картинки до нужного размера сразу после декодирования JPEG-а, тогда объём памяти не должен зависеть от первоначального размера картинки и для Thumb-ов размером 128*80 будет где-то 30Кб на картинку, то есть <1М в вашем случае.
- как определить размер картинки (если картинка маленькая то при Scale=jsEighth она еле различимая)
У IJL есть соответствующий режим, а стандартный TJPEGImage и так декодирует размер картинки, то есть после TJPEGImage.LoadFromFile размер (Width и Height) будет иметь правильное значение, но сама картинка декодирована не будет, а собственно распаковка картинки происходит в момент обращения к DIBNeeded.
Вам надо вызвать LoadFromFile, в зависимости от размеров выставить Scale, вызвать DIBNeeded, перенести картинку на Image c помощью StretchDraw.

07-02-2006 03:15
Чисто предположение. Может попробовать покопать в сторону GDI+.

07-02-2006 01:55 | Сообщение от автора вопроса
to DRON:
Спасибо за помощь!!! Пробовал "ijl15.dll" но скорость как у стандартных средств (TJPEGImage).

Сейчас у меня другие задачи:
- как сделать чтобы картинки занимали меньше места в памяти (сейчс после загрузки 30 jpeg'ов программа раздувается до 500мб :)))
- как определить размер картинки (если картинка маленькая то при Scale=jsEighth она еле различимая)

03-02-2006 13:12
Вот нашёл в закромах пару ссылок:
собственно библиотека (есть пример на Delphi)
http://www.sources.ru/cpp/IJL1.5BW-B7.exe
некая простая оболочка (LoadBmpFromJpegFile) над IJL.PAS
http://www.david-taylor.pwp.blueyonder.co.uk/software/JPEG_IO.zip

Уменьшать можно только до 8 раз, так как уменьшение идёт на уровне блоков, а их размер 8x8.

03-02-2006 10:51
нельзя ли загружать картинку уменьшенной в 16, 32 раз?
Надо документацию по jpeglib найти и посмотреть какие значения могут принимать scale_num и scale_denom, но думаю, что больше чем 8 не получится.
Посмотрите всё таки "ijl15.dll" (Intel JPEG Library) думаю это наиболее быстрая библиотека (её ACDSee использует).

03-02-2006 09:30 | Сообщение от автора вопроса
to DRON:
Спасибо, заработало! Намного быстрей, только есть одно но... Если размер jpg оооооооочень большой (7-10 Mb) все равно медленно, нельзя ли загружать картинку уменьшенной в 16, 32 раз?

to karai2:
Если по другому не получится буду юзать Thread'ы. Хотя ни разу не пробовал :)))

Никто не знает какую-то ссылку по потокам для начинающих??

03-02-2006 07:27
нужно производить подгрузку изображений в другом потоке (в смысле Thread)
однако, если раньше с ним не работали, сложновато будет...



03-02-2006 03:49 | Сообщение от автора вопроса
to Zloxa:
Windows даже в самый первый раз подгружает их практически сразу.

А как мне сделать чтобы они загружались в фоновом режиме??

to DRON:
Надо попробовать, спасибо!!!

03-02-2006 03:28
Поиграйтесь с TJPEGScale, у TJPEGImage есть возможность загружать картинку сразу уменьшенной в 2,4 или 8 раз. Ещё можно Performance=jpBestSpeed и Smoothing=False.

03-02-2006 03:13
дык винда же подгружает жипеги в фоновом потоке, пока пользователь тупо думает что ж ему дальше делать папкой, куда он зашел. Ну а после подгрузки создает иконочную копию изображения, которую сливает в Thumbs.db, соответственно последующие загрузки происходят ойкак быстро.

Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Кто съел Красную шапочку?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.
Тип сообщения:
Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве

    Страница избранных вопросов Круглого стола.
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования