Счетчик HotLog

На главнуюЧто я делаю...Программы автора и не только...Творчество
Статьи автораКнига отзывов и предложенийОбо мне, любимомФотоальбом

 
 

Генератор фотоальбомов PhotoalbumGenerator

Серьезное удешевление цифровой фототехники привело к тому, что наши (надеюсь, что не я один столкнулся с такой проблемой) домашние компьютеры просто завалены фотографиями, мертвым, по сути, грузом, так как сделать из скопища цифрового мусора нечто удобоваримое часто просто не хватает времени. Вот и лежат фотографии большой кучей, к которой и прикасаться-то неприятно. Здесь мы вместе напишем код этой программы. Тем, кто не собирается осваивать премудрости программирования, можно просто скачать программу. В ином случае, я приглашаю вас вместе со мной приступить к непростому делу программирования.

Программировать будем в CodeGear Delphi for Win32.

Итак начнем. Любой серьезный подход предполагает взвешенное и продуманное действие, поэтому мы начнем с этапа

Проектирование

Для начала определимся с результатами работы. Я их описал так: набор html-страниц, на главной странице есть список из уменьшенных копий всех фотографий альбома, на каждой странице представлена более крупная копия фотографии, со страницы при желании можно выйти на оригинал фотографии (большой и тяжелый). Такой формат выбран из соображений простоты публикации в интернете и удобного просмотра на локальной машине.

Из задачи вытекают классы, которые позволят решить задачу. Их (основных, видимых еще на этапе проектирования) четыре:

  • генератор страниц;
  • класс настроек;
  • класс единицы фотоальбома;
  • класс преобразования изображений.

На этом, поскольку задачка достаточно простая, считаю возможным этап проектирования завершить и приступить к следующему этапу.

Программирование

Начать предлагаю с самого сложного (в техническом исполнении) класса - класса преобразования изображений.

Что должен уметь этот класс? С моей точки зрения, немногое: получать на вход исходное изображение, изменять его размер и сохранять в указанное место с указанным качеством. Таким образом, класс будет обладать всего одним публичным методом:

  TMPJpegFileResizer = class(TObject)
  public
    // Изменить размер рисунка и сохранить.
    class procedure ResizeAndSave(
      // Изображение.
      const AImage: Graphics.TBitmap;
      // Имя файла (куда следует сохранить готовое изображение.)
      const AFileName: String;
      // Размер изображения.
      const ASize: Word;
      // Качество сохранения (по умолчанию 75).
      const AQuality: Word = 75);
  end;

Алгоритм преобразования такой: проверить, есть ли необходимость в изменении размеров изображения, создать изображение меньшего размера и сохранить его. В результате у меня получился вот такой модуль:

// Модуль изменения размеров изображения.
unit MPJpegFileResizer;

interface

uses
  Graphics, Types;

type
  // Измерения изображения.
  TMPDimensions = TPoint;

  // Класс изменения размеров изображения.
  TMPJpegFileResizer = class(TObject)
  protected
    // Получить измерения изображения.
    class function GetDimensions(
      // Изображение.
      const AImage: Graphics.TBitmap;
      // Размер изображения.
      const ASize: Word): TMPDimensions;
    // Сохранить изображение в файл.
    class procedure SaveJPEGPictureFile(
      // Изображение.
      const AImage: Graphics.TBitmap;
      // Имя графического файла.
      const AFileName: String;
      // Качество сохранения изображения.
      const AQuality: Integer);
    // Установить размеры изображению.
    class procedure SetDimensions(
      // Изображение.
      const AImage: Graphics.TBitmap;
      // Размеры изображения.
      const ADimensions: TMPDimensions);
    // Выполнить ровное изменение размера.
    class procedure SmoothResize(
      // Исходное изображение.
      const ASourceImage: Graphics.TBitmap;
      // Конечное изображение.
      const ADestinationImage: Graphics.TBitmap);
  public
    // Изменить размер рисунка и сохранить.
    class procedure ResizeAndSave(
      // Изображение.
      const AImage: Graphics.TBitmap;
      // Имя файла (куда следует сохранить готовое изображение).
      const AFileName: String;
      // Размер изображения.
      const ASize: Word;
      // Качество сохранения (по умолчанию 75).
      const AQuality: Word = 75);
  end;

implementation

uses
  JPEG, SysUtils, Windows;

type
  // Массив цветовых составляющих.
  TRGBArray = array[Word] of TRGBTriple;
  // Указатель на массив цветовых составляющих.
  PRGBArray = ^TRGBArray;

{ TMPJpegFileResizer }

class function TMPJpegFileResizer.GetDimensions(const AImage: Graphics.TBitmap;
  const ASize: Word): TMPDimensions;
begin
  if AImage.Width > AImage.Height then
  begin
    Result.X := ASize;
    Result.Y := AImage.Height * ASize div AImage.Width;
  end
  else
  begin
    Result.X := AImage.Width * ASize div AImage.Height;
    Result.Y := ASize;
  end;
end;

class procedure TMPJpegFileResizer.ResizeAndSave(const AImage: Graphics.TBitmap;
  const AFileName: String; const ASize, AQuality: Word);
var
  NewImage: Graphics.TBitmap;
begin
  if (AImage.Width > ASize) or (AImage.Height > ASize) then
  begin
    NewImage := Graphics.TBitmap.Create;
    try
      TMPJpegFileResizer.SetDimensions(NewImage,
        TMPJpegFileResizer.GetDimensions(AImage, ASize));
      TMPJpegFileResizer.SmoothResize(AImage, NewImage);
      TMPJpegFileResizer.SaveJPEGPictureFile(NewImage, AFileName, AQuality);
    finally
      NewImage.Free;
    end;
  end;
end;

class procedure TMPJpegFileResizer.SaveJPEGPictureFile(
  const AImage: Graphics.TBitmap; const AFileName: String;
  const AQuality: Integer);
begin
  ForceDirectories(ExtractFilePath(AFileName));
  with TJPegImage.Create do
  begin
    try
      Assign(AImage);
      CompressionQuality := AQuality;
      SaveToFile(AFileName);
    finally
      Free;
    end;
  end;
end;

class procedure TMPJpegFileResizer.SetDimensions(const AImage: Graphics.TBitmap;
  const ADimensions: TMPDimensions);
begin
  AImage.Width := ADimensions.X;
  AImage.Height := ADimensions.Y;
end;

class procedure TMPJpegFileResizer.SmoothResize(const ASourceImage,
  ADestinationImage: Graphics.TBitmap);
var
  x, y, xP, yP, xP2, yP2, t3, z, z2, iz2, DstGap, w1, w2, w3, w4: Integer;
  SrcLine1, SrcLine2, DstLine: PRGBArray;
begin
  ASourceImage.PixelFormat := pf24Bit;
  ADestinationImage.PixelFormat := pf24Bit;
  if (ASourceImage.Width = ADestinationImage.Width) and
     (ASourceImage.Height = ADestinationImage.Height) then
    ADestinationImage.Assign(ASourceImage)
  else
  begin
    DstLine := ADestinationImage.ScanLine[0];
    DstGap  := Integer(ADestinationImage.ScanLine[1]) - Integer(DstLine);
    xP2 := MulDiv(Pred(ASourceImage.Width), $10000, ADestinationImage.Width);
    yP2 := MulDiv(Pred(ASourceImage.Height), $10000, ADestinationImage.Height);
    yP  := 0;
    for y := 0 to Pred(ADestinationImage.Height) do
    begin
      xP := 0;
      SrcLine1 := ASourceImage.ScanLine[yP shr 16];
      if (yP shr 16 < Pred(ASourceImage.Height)) then
        SrcLine2 := ASourceImage.ScanLine[Succ(yP shr 16)]
      else
        SrcLine2 := ASourceImage.ScanLine[yP shr 16];
      z2  := Succ(yP and $FFFF);
      iz2 := Succ((not yp) and $FFFF);
      for x := 0 to Pred(ADestinationImage.Width) do
      begin
        t3 := xP shr 16;
        z  := xP and $FFFF;
        w2 := MulDiv(z, iz2, $10000);
        w1 := iz2 - w2;
        w4 := MulDiv(z, z2, $10000);
        w3 := z2 - w4;
        DstLine[x].rgbtRed :=
          (SrcLine1[t3].rgbtRed * w1 + SrcLine1[t3 + 1].rgbtRed * w2 +
          SrcLine2[t3].rgbtRed * w3 + SrcLine2[t3 + 1].rgbtRed * w4) shr 16;
        DstLine[x].rgbtGreen :=
          (SrcLine1[t3].rgbtGreen * w1 + SrcLine1[t3 + 1].rgbtGreen * w2 +
          SrcLine2[t3].rgbtGreen * w3 + SrcLine2[t3 + 1].rgbtGreen * w4) shr 16;
        DstLine[x].rgbtBlue :=
          (SrcLine1[t3].rgbtBlue * w1 + SrcLine1[t3 + 1].rgbtBlue * w2 +
          SrcLine2[t3].rgbtBlue * w3 + SrcLine2[t3 + 1].rgbtBlue * w4) shr 16;
        Inc(xP, xP2);
      end;
      Inc(yP, yP2);
      DstLine := PRGBArray(Integer(DstLine) + DstGap);
    end;
  end;
end;

end.

В результате у меня получился вот такой модуль:

Скачать исходный код программы: 

Скачать исполняемый файл: 

 
 

20.02.2008

 
     
Hosted by uCoz