Повернуть растровое изображение на реальный угол

28

Когда-то, читая этот вопрос, я задавался вопросом, как повернуть растровое изображение в любом случае, не путаясь со всеми битами. В последнее время кто-то другой также сталкивался с определенными трудностями.

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

procedure RotateBitmap(Bmp: TBitmap; Angle: Single; AdjustSize: Boolean; 
  BackColor: TColor);

В этих ответах упоминаются следующие кандидаты на создание этой подпрограммы: SetWorldTransform, PlgBlt, GDI +, но я хотел бы видеть (эффективную) реализацию.

  • 2
    Вы можете использовать заголовок как Поворот растрового изображения на реальный угол, годовщину издания :-)
Теги:
image-processing
rotation

2 ответа

50
Лучший ответ

tl; dr; Использовать GDI +

SetWorldTransform

С помощью WinAPI SetWorldTransform вы можете преобразовать пространство контекста устройства: вращать, сдвигать, смещать и масштабировать. Это делается путем установки элементов матрицы преобразования типа XFORM. Заполните его элементы в соответствии с документацией.

procedure RotateBitmap(Bmp: TBitmap; Rads: Single; AdjustSize: Boolean;
  BkColor: TColor = clNone);
var
  C: Single;
  S: Single;
  XForm: tagXFORM;
  Tmp: TBitmap;
begin
  C := Cos(Rads);
  S := Sin(Rads);
  XForm.eM11 := C;
  XForm.eM12 := S;
  XForm.eM21 := -S;
  XForm.eM22 := C;
  Tmp := TBitmap.Create;
  try
    Tmp.TransparentColor := Bmp.TransparentColor;
    Tmp.TransparentMode := Bmp.TransparentMode;
    Tmp.Transparent := Bmp.Transparent;
    Tmp.Canvas.Brush.Color := BkColor;
    if AdjustSize then
    begin
      Tmp.Width := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
      Tmp.Height := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
      XForm.eDx := (Tmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      XForm.eDy := (Tmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end
    else
    begin
      Tmp.Width := Bmp.Width;
      Tmp.Height := Bmp.Height;
      XForm.eDx := (Bmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      XForm.eDy := (Bmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end;
    SetGraphicsMode(Tmp.Canvas.Handle, GM_ADVANCED);
    SetWorldTransform(Tmp.Canvas.Handle, XForm);
    BitBlt(Tmp.Canvas.Handle, 0, 0, Tmp.Width, Tmp.Height, Bmp.Canvas.Handle,
      0, 0, SRCCOPY);
    Bmp.Assign(Tmp);
  finally
    Tmp.Free;
  end;
end;

PlgBlt

Функция PlgBlt выполняет перенос битового блока из указанного прямоугольника в контексте исходного устройства на указанный параллелограмм в контексте целевого устройства. Сопоставьте угловые точки исходного изображения с помощью параметра lpPoint.

procedure RotateBitmap(Bmp: TBitmap; Rads: Single; AdjustSize: Boolean;
  BkColor: TColor = clNone);
var
  C: Single;
  S: Single;
  Tmp: TBitmap;
  OffsetX: Single;
  OffsetY: Single;
  Points: array[0..2] of TPoint;
begin
  C := Cos(Rads);
  S := Sin(Rads);
  Tmp := TBitmap.Create;
  try
    Tmp.TransparentColor := Bmp.TransparentColor;
    Tmp.TransparentMode := Bmp.TransparentMode;
    Tmp.Transparent := Bmp.Transparent;
    Tmp.Canvas.Brush.Color := BkColor;
    if AdjustSize then
    begin
      Tmp.Width := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
      Tmp.Height := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
      OffsetX := (Tmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      OffsetY := (Tmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end
    else
    begin
      Tmp.Width := Bmp.Width;
      Tmp.Height := Bmp.Height;
      OffsetX := (Bmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      OffsetY := (Bmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end;
    Points[0].X := Round(OffsetX);
    Points[0].Y := Round(OffsetY);
    Points[1].X := Round(OffsetX + Bmp.Width * C);
    Points[1].Y := Round(OffsetY + Bmp.Width * S);
    Points[2].X := Round(OffsetX - Bmp.Height * S);
    Points[2].Y := Round(OffsetY + Bmp.Height * C);
    PlgBlt(Tmp.Canvas.Handle, Points, Bmp.Canvas.Handle, 0, 0, Bmp.Width,
      Bmp.Height, 0, 0, 0);
    Bmp.Assign(Tmp);
  finally
    Tmp.Free;
  end;
end;

Graphics32

Graphics32 - это библиотека, специально разработанная для быстрой обработки растровых изображений. Это требует некоторого опыта, чтобы понять весь свой потенциал, но документация, а также приведенные примеры должны помочь вам начать.

Вращение изображения TBitmap32 выполняется путем преобразования его одним из многих доступных классов преобразования. Здесь нужен класс TAffineTransformation. Сначала сдвиньте изображение на половину его размера в верхнее левое положение, затем поверните и сдвиньте результат обратно в нижний правый угол, возможно используя новые размеры изображения.

uses
  GR32, GR32_Transforms;

procedure RotateBitmap(Bmp: TBitmap32; Degs: Integer; AdjustSize: Boolean;
  BkColor: TColor = clNone; Transparent: Boolean = False); overload;
var
  Tmp: TBitmap32;
  Transformation: TAffineTransformation;
begin
  Tmp := TBitmap32.Create;
  Transformation := TAffineTransformation.Create;
  try
    Transformation.BeginUpdate;
    Transformation.SrcRect := FloatRect(0, 0, Bmp.Width, Bmp.Height);
    Transformation.Translate(-0.5 * Bmp.Width, -0.5 * Bmp.Height);
    Transformation.Rotate(0, 0, -Degs);
    if AdjustSize then
      with Transformation.GetTransformedBounds do
        Tmp.SetSize(Round(Right - Left), Round(Bottom - Top))
    else
      Tmp.SetSize(Bmp.Width, Bmp.Height);
    Transformation.Translate(0.5 * Tmp.Width, 0.5 * Tmp.Height);
    Transformation.EndUpdate;
    Tmp.Clear(Color32(BkColor));
    if not Transparent then
      Bmp.DrawMode := dmTransparent;
    Transform(Tmp, Bmp, Transformation);
    Bmp.Assign(Tmp);
    Bmp.OuterColor := Color32(BkColor);
    if Transparent then
      Bmp.DrawMode := dmTransparent;
  finally
    Transformation.Free;
    Tmp.Free;
  end;
end;

procedure RotateBitmap(Bmp: TBitmap; Degs: Integer; AdjustSize: Boolean;
  BkColor: TColor = clNone); overload;
var
  Tmp: TBitmap32;
  Transparent: Boolean;
begin
  Tmp := TBitmap32.Create;
  try
    Transparent := Bmp.Transparent;
    Tmp.Assign(Bmp);
    RotateBitmapGR32(Tmp, Degs, AdjustSize, BkColor, Transparent);
    Bmp.Assign(Tmp);
    if Transparent then
      Bmp.Transparent := True;
  finally
    Tmp.Free;
  end;
end;

GDI +

Представленный в Windows XP, Microsoft API GDI + более эффективен, чем API GDI по умолчанию. Для Delphi 2009 и выше библиотека доступна здесь. Для более старых версий Delphi библиотека доступна здесь.

В GDI + вращение также выполняется с помощью матрицы преобразования. Рисование работает совсем по-другому. Создайте объект TGPGraphics и присоедините его к контексту устройства со своим конструктором. Впоследствии операции рисования объекта преобразуются API и будут выводиться в контекст назначения.

uses
  GDIPOBJ, GDIPAPI; // < D2009
  GdiPlus;          // >= D2009

procedure RotateBitmap(Bmp: TBitmap; Degs: Integer; AdjustSize: Boolean;
  BkColor: TColor = clNone);
var
  Tmp: TGPBitmap;
  Matrix: TGPMatrix;
  C: Single;
  S: Single;
  NewSize: TSize;
  Graphs: TGPGraphics;
  P: TGPPointF;
begin
  Tmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette);
  Matrix := TGPMatrix.Create;
  try
    Matrix.RotateAt(Degs, MakePoint(0.5 * Bmp.Width, 0.5 * Bmp.Height));
    if AdjustSize then
    begin
      C := Cos(DegToRad(Degs));
      S := Sin(DegToRad(Degs));
      NewSize.cx := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
      NewSize.cy := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
      Bmp.Width := NewSize.cx;
      Bmp.Height := NewSize.cy;
    end;
    Graphs := TGPGraphics.Create(Bmp.Canvas.Handle);
    try
      Graphs.Clear(ColorRefToARGB(ColorToRGB(BkColor)));
      Graphs.SetTransform(Matrix);
      Graphs.DrawImage(Tmp, (Cardinal(Bmp.Width) - Tmp.GetWidth) div 2,
        (Cardinal(Bmp.Height) - Tmp.GetHeight) div 2);
    finally
      Graphs.Free;
    end;
  finally
    Matrix.Free;
    Tmp.Free;
  end;
end;

Обработка прозрачности

Приведенные выше процедуры сохраняют прозрачные настройки растрового изображения fead, за исключением решения Graphics32, для которого требуется дополнительный параметр Transparent.

Производительность и качество изображения

Я написал тестовое приложение (см. полный код ниже), чтобы настроить производительность различных методов и сравнить полученное качество изображения.

Первый и самый важный вывод заключается в том, что GDI + использует сглаживание, а другие нет, что приводит к лучшему качеству изображения. (Я безуспешно пытался предотвратить сглаживание, установив CompositingQuality, InterpolationMode, SmoothingMode и PixelOffsetMode, поэтому, когда сглаживание не является предпочтительным, не используйте GDI +.)

Кроме того, решение GDI + также является самым быстрым методом.

Изображение 7798

unit RotateTestForm;

interface

uses
  Windows, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls,
  JPEG, Math, GR32, GR32_Transforms, GDIPOBJ, GDIPAPI {, GdiPlus};

type
  TTestForm = class(TForm)
  private
    FImage: TImage;
    FOpenDialog: TOpenDialog;
    procedure FormPaint(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
  end;

var
  TestForm: TTestForm;

implementation

{$R *.dfm}

procedure RotateBitmapSWT(Bmp: TBitmap; Rads: Single; AdjustSize: Boolean;
  BkColor: TColor = clNone);
var
  C: Single;
  S: Single;
  XForm: TXForm;
  Tmp: TBitmap;
begin
  C := Cos(Rads);
  S := Sin(Rads);
  XForm.eM11 := C;
  XForm.eM12 := S;
  XForm.eM21 := -S;
  XForm.eM22 := C;
  Tmp := TBitmap.Create;
  try
    Tmp.TransparentColor := Bmp.TransparentColor;
    Tmp.TransparentMode := Bmp.TransparentMode;
    Tmp.Transparent := Bmp.Transparent;
    Tmp.Canvas.Brush.Color := BkColor;
    if AdjustSize then
    begin
      Tmp.Width := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
      Tmp.Height := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
      XForm.eDx := (Tmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      XForm.eDy := (Tmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end
    else
    begin
      Tmp.Width := Bmp.Width;
      Tmp.Height := Bmp.Height;
      XForm.eDx := (Bmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      XForm.eDy := (Bmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end;
    SetGraphicsMode(Tmp.Canvas.Handle, GM_ADVANCED);
    SetWorldTransform(Tmp.Canvas.Handle, XForm);
    BitBlt(Tmp.Canvas.Handle, 0, 0, Tmp.Width, Tmp.Height, Bmp.Canvas.Handle,
      0, 0, SRCCOPY);
    Bmp.Assign(Tmp);
  finally
    Tmp.Free;
  end;
end;

procedure RotateBitmapPLG(Bmp: TBitmap; Rads: Single; AdjustSize: Boolean;
  BkColor: TColor = clNone);
var
  C: Single;
  S: Single;
  Tmp: TBitmap;
  OffsetX: Single;
  OffsetY: Single;
  Points: array[0..2] of TPoint;
begin
  C := Cos(Rads);
  S := Sin(Rads);
  Tmp := TBitmap.Create;
  try
    Tmp.TransparentColor := Bmp.TransparentColor;
    Tmp.TransparentMode := Bmp.TransparentMode;
    Tmp.Transparent := Bmp.Transparent;
    Tmp.Canvas.Brush.Color := BkColor;
    if AdjustSize then
    begin
      Tmp.Width := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
      Tmp.Height := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
      OffsetX := (Tmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      OffsetY := (Tmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end
    else
    begin
      Tmp.Width := Bmp.Width;
      Tmp.Height := Bmp.Height;
      OffsetX := (Bmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
      OffsetY := (Bmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
    end;
    Points[0].X := Round(OffsetX);
    Points[0].Y := Round(OffsetY);
    Points[1].X := Round(OffsetX + Bmp.Width * C);
    Points[1].Y := Round(OffsetY + Bmp.Width * S);
    Points[2].X := Round(OffsetX - Bmp.Height * S);
    Points[2].Y := Round(OffsetY + Bmp.Height * C);
    PlgBlt(Tmp.Canvas.Handle, Points, Bmp.Canvas.Handle, 0, 0, Bmp.Width,
      Bmp.Height, 0, 0, 0);
    Bmp.Assign(Tmp);
  finally
    Tmp.Free;
  end;
end;

procedure RotateBitmapGR32(Bmp: TBitmap32; Degs: Integer; AdjustSize: Boolean;
  BkColor: TColor = clNone; Transparent: Boolean = False); overload;
var
  Tmp: TBitmap32;
  Transformation: TAffineTransformation;
begin
  Tmp := TBitmap32.Create;
  Transformation := TAffineTransformation.Create;
  try
    Transformation.BeginUpdate;
    Transformation.SrcRect := FloatRect(0, 0, Bmp.Width, Bmp.Height);
    Transformation.Translate(-0.5 * Bmp.Width, -0.5 * Bmp.Height);
    Transformation.Rotate(0, 0, -Degs);
    if AdjustSize then
      with Transformation.GetTransformedBounds do
        Tmp.SetSize(Round(Right - Left), Round(Bottom - Top))
    else
      Tmp.SetSize(Bmp.Width, Bmp.Height);
    Transformation.Translate(0.5 * Tmp.Width, 0.5 * Tmp.Height);
    Transformation.EndUpdate;
    Tmp.Clear(Color32(BkColor));
    if not Transparent then
      Bmp.DrawMode := dmTransparent;
    Transform(Tmp, Bmp, Transformation);
    Bmp.Assign(Tmp);
    Bmp.OuterColor := Color32(BkColor);
    if Transparent then
      Bmp.DrawMode := dmTransparent;
  finally
    Transformation.Free;
    Tmp.Free;
  end;
end;

procedure RotateBitmapGR32(Bmp: TBitmap; Degs: Integer; AdjustSize: Boolean;
  BkColor: TColor = clNone); overload;
var
  Tmp: TBitmap32;
  Transparent: Boolean;
begin
  Tmp := TBitmap32.Create;
  try
    Transparent := Bmp.Transparent;
    Tmp.Assign(Bmp);
    RotateBitmapGR32(Tmp, Degs, AdjustSize, BkColor, Transparent);
    Bmp.Assign(Tmp);
    if Transparent then
      Bmp.Transparent := True;
  finally
    Tmp.Free;
  end;
end;

procedure RotateBitmapGDIP(Bmp: TBitmap; Degs: Integer; AdjustSize: Boolean;
  BkColor: TColor = clNone);
var
  Tmp: TGPBitmap;
  Matrix: TGPMatrix;
  C: Single;
  S: Single;
  NewSize: TSize;
  Graphs: TGPGraphics;
  P: TGPPointF;
begin
  Tmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette);
  Matrix := TGPMatrix.Create;
  try
    Matrix.RotateAt(Degs, MakePoint(0.5 * Bmp.Width, 0.5 * Bmp.Height));
    if AdjustSize then
    begin
      C := Cos(DegToRad(Degs));
      S := Sin(DegToRad(Degs));
      NewSize.cx := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
      NewSize.cy := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
      Bmp.Width := NewSize.cx;
      Bmp.Height := NewSize.cy;
    end;
    Graphs := TGPGraphics.Create(Bmp.Canvas.Handle);
    try
      Graphs.Clear(ColorRefToARGB(ColorToRGB(BkColor)));
      Graphs.SetTransform(Matrix);
      Graphs.DrawImage(Tmp, (Cardinal(Bmp.Width) - Tmp.GetWidth) div 2,
        (Cardinal(Bmp.Height) - Tmp.GetHeight) div 2);
    finally
      Graphs.Free;
    end;
  finally
    Matrix.Free;
    Tmp.Free;
  end;
end;

{ TTestForm }

constructor TTestForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Font.Name := 'Tahoma';
  Top := 0;
  ClientWidth := 560;
  ClientHeight := 915;
  Show;
  FImage := TImage.Create(Self);
  FOpenDialog := TOpenDialog.Create(Self);
  FOpenDialog.Title := 'Select an small sized image (min. 100 x 100)';
  FOpenDialog.Options := FOpenDialog.Options + [ofFileMustExist];
  FOpenDialog.Filter := 'JPEG|*.JPG|BMP|*.BMP';
  if FOpenDialog.Execute then
  begin
    FImage.Picture.LoadFromFile(FOpenDialog.FileName);
    OnPaint := FormPaint;
    Invalidate;
  end
  else
    Application.Terminate;
end;

procedure TTestForm.FormPaint(Sender: TObject);
var
  Img: TBitmap;
  Bmp: TBitmap;
  Bmp32: TBitmap32;
  BkColor: TColor;
  AdjustSize: Boolean;
  Degs: Integer;
  Rads: Single;
  RotCount: Integer;
  I: Integer;
  Tick: Cardinal;
begin
  Img := TBitmap.Create;
  Bmp := TBitmap.Create;
  Bmp32 := TBitmap32.Create;
  try
    BkColor := clBtnFace;
    Img.Canvas.Brush.Color := BkColor;
    Img.Width := 100;
    Img.Height := 100;
    Img.Canvas.Draw(0, 0, FImage.Picture.Graphic);
    AdjustSize := False;
    Degs := 45;
    Rads := DegToRad(Degs);
    RotCount := 1000;

    Canvas.TextOut(10, 10, 'Original:');
    Canvas.Draw(10, 30, Img);
    Canvas.TextOut(10, 140, Format('Size = %d x %d', [Img.Width, Img.Height]));
    Canvas.TextOut(10, 160, Format('Angle = %d°', [Degs]));
    Canvas.TextOut(10, 250, Format('%d rotations:', [RotCount]));

    Canvas.TextOut(120, 10, 'SetWorldTransform:');
    Bmp.Assign(Img);
    RotateBitmapSWT(Bmp, Rads, AdjustSize, BkColor);
    Canvas.Draw(120, 30, Bmp);
    if not AdjustSize then
    begin
      Tick := GetTickCount;
      for I := 0 to RotCount - 2 do
        RotateBitmapSWT(Bmp, Rads, AdjustSize, BkColor);
      Canvas.TextOut(120, 250, Format('%d msec', [GetTickCount - Tick]));
      Canvas.Draw(120, 140, Bmp);
    end;

    Canvas.TextOut(230, 10, 'PlgBlt:');
    Bmp.Assign(Img);
    RotateBitmapPLG(Bmp, Rads, AdjustSize, BkColor);
    Canvas.Draw(230, 30, Bmp);
    if not AdjustSize then
    begin
      Tick := GetTickCount;
      for I := 0 to RotCount - 2 do
        RotateBitmapPLG(Bmp, Rads, AdjustSize, BkColor);
      Canvas.TextOut(230, 250, Format('%d msec', [GetTickCount - Tick]));
      Canvas.Draw(230, 140, Bmp);
    end;

    Canvas.TextOut(340, 10, 'Graphics32:');
    Bmp.Assign(Img);
    RotateBitmapGR32(Bmp, Degs, AdjustSize, BkColor);
    Canvas.Draw(340, 30, Bmp);
    if not AdjustSize then
    begin
      Tick := GetTickCount;
      for I := 0 to RotCount - 2 do
        RotateBitmapGR32(Bmp, Degs, AdjustSize, BkColor);
      Canvas.TextOut(340, 250, Format('%d msec', [GetTickCount - Tick]));
      Canvas.Draw(340, 140, Bmp);

      // Without in between conversion to TBitmap:
      Bmp32.Assign(Img);
      Tick := GetTickCount;
      for I := 0 to RotCount - 1 do
        RotateBitmapGR32(Bmp32, Degs, AdjustSize, BkColor, False);
      Canvas.TextOut(340, 270, Format('%d msec (optimized)',
        [GetTickCount - Tick]));
    end;

    Canvas.TextOut(450, 10, 'GDI+ :');
    Bmp.Assign(Img);
    RotateBitmapGDIP(Bmp, Degs, AdjustSize, BkColor);
    Canvas.Draw(450, 30, Bmp);
    if not AdjustSize then
    begin
      Tick := GetTickCount;
      for I := 0 to RotCount - 2 do
        RotateBitmapGDIP(Bmp, Degs, AdjustSize, BkColor);
      Canvas.TextOut(450, 250, Format('%d msec', [GetTickCount - Tick]));
      Canvas.Draw(450, 140, Bmp);
    end;
  finally
    Bmp32.Free;
    Bmp.Free;
    Img.Free;
    OnPaint := nil;
  end;
end;

end.
  • 0
    Теперь, глядя на внутренние компоненты this library , она также может стать одним из самых популярных фаворитов для этого теста ... А также результаты сглаживания looks impressive (я должен попробовать это :-)
  • 0
    @TLama - Привет, TLama. Каковы выводы этой библиотеки? «Поворот» это лучше (плавнее, быстрее), чем код выше? Благодарю.
Показать ещё 9 комментариев
3

Если кто-то ищет поворот изображения, они также могут посмотреть видео-библиотеку Mitov (бесплатно для некоммерческого использования: ссылка). VCL и FireMonkey. Он заботится обо всех деталях низкого уровня, что позволяет нам избегать подробного кодирования, которое исследует превосходный ответ NGLN.

Мы использовали его в течение последних двух лет и были очень довольны этим в нашем коммерческом приложении.

Он имеет компонент rotate, который работает со статическими изображениями и видеопотоками. Их библиотека полностью многозадачна, опционально использует все основные и примитивные примитивы низкого уровня на чипсетах Intel с собственной библиотекой производительности Intel (http://software.intel.com/en-us/articles/intel-ipp)

На умеренном оборудовании мы можем запускать несколько потоков видео или BMP, которые мы вращаем, клип, масштабируем и обрабатываем на уровне пикселей в режиме реального времени.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню