JPEG 图像的无损转换

压缩 JPEG 图像时,图像中的某些信息将丢失。 如果打开 JPEG 文件,更改图像,并将其保存到另一个 JPEG 文件,则质量将降低。 如果多次重复该过程,将看到图像质量大幅下降。

由于 JPEG 是 Web 上最常用的图像格式之一,并且人们经常喜欢修改 JPEG 图像,因此 GDI+ 提供了可在 JPEG 图像上执行的以下转换,而不会丢失信息:

  • 旋转 90 度
  • 旋转 180 度
  • 旋转 270 度
  • 水平翻转
  • 垂直翻转

调用 Image 对象的 Save 方法时,可以应用上述列表中所示的转换之一。 如果满足以下条件,则转换将继续,而不会丢失信息:

  • 用于构造 Image 对象的文件是 JPEG 文件。
  • 图像的宽度和高度都是 16 的倍数。

如果图像的宽度和高度不是 16 的倍数,则当你应用上述列表中显示的旋转或翻转转换之一时,GDI+ 将尽其所能保持图像质量。

若要转换 JPEG 图像,请初始化 EncoderParameters 对象,并将该对象的地址传递给 Image 类的 Save 方法。 初始化 EncoderParameters 对象,使其具有包含一个 EncoderParameter 对象的数组。 初始化一个 EncoderParameter 对象,使其 Value 成员指向保存 EncoderValue 枚举的下列元素之一的 ULONG 变量:

  • EncoderValueTransformRotate90,
  • EncoderValueTransformRotate180,
  • EncoderValueTransformRotate270,
  • EncoderValueTransformFlipHorizontal,
  • EncoderValueTransformFlipVertical

EncoderParameter 对象的 Guid 成员设置为 EncoderTransformation。

以下控制台应用程序从 JPEG 文件创建 Image 对象,然后将该映像保存到新文件。 在保存过程中,图像旋转 90 度。 如果图像的宽度和高度均为 16 的倍数,则旋转和保存图像的过程不会丢失信息。

main 函数依赖于帮助程序函数 GetEncoderClsid,该函数显示在检索编码器的类标识符中。

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;

INT GetEncoderClsid(const WCHAR* format, CLSID* pClsid);  // helper function

INT main()
{
   // Initialize GDI+.
   GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

   CLSID             encoderClsid;
   EncoderParameters encoderParameters;
   ULONG             transformation;
   UINT              width;
   UINT              height;
   Status            stat;

   // Get a JPEG image from the disk.
   Image* image = new Image(L"Shapes.jpg");

   // Determine whether the width and height of the image 
   // are multiples of 16.
   width = image->GetWidth();
   height = image->GetHeight();

   printf("The width of the image is %u", width);
   if(width / 16.0 - width / 16 == 0)
      printf(", which is a multiple of 16.\n");
   else
      printf(", which is not a multiple of 16.\n");

   printf("The height of the image is %u", height);
   if(height / 16.0 - height / 16 == 0)
      printf(", which is a multiple of 16.\n");
   else
      printf(", which is not a multiple of 16.\n");

   // Get the CLSID of the JPEG encoder.
   GetEncoderClsid(L"image/jpeg", &encoderClsid);

   // Before we call Image::Save, we must initialize an
   // EncoderParameters object. The EncoderParameters object
   // has an array of EncoderParameter objects. In this
   // case, there is only one EncoderParameter object in the array.
   // The one EncoderParameter object has an array of values.
   // In this case, there is only one value (of type ULONG)
   // in the array. We will set that value to EncoderValueTransformRotate90.

   encoderParameters.Count = 1;
   encoderParameters.Parameter[0].Guid = EncoderTransformation;
   encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
   encoderParameters.Parameter[0].NumberOfValues = 1;

   // Rotate and save the image.
   transformation = EncoderValueTransformRotate90;
   encoderParameters.Parameter[0].Value = &transformation;
   stat = image->Save(L"ShapesR90.jpg", &encoderClsid, &encoderParameters);

   if(stat == Ok)
      wprintf(L"%s saved successfully.\n", L"ShapesR90.jpg");
   else
      wprintf(L"%d  Attempt to save %s failed.\n", stat, L"ShapesR90.jpg");

   delete image;
   GdiplusShutdown(gdiplusToken);
   return 0;
}