Transforming a JPEG Image Without Loss of Information
When you compress a JPEG image, some of the information in the image is lost. If you open a JPEG file, alter the image, and save it to another JPEG file, the quality will decrease. If you repeat that process many times, you will see a substantial degradation in the image quality.
Because JPEG is one of the most popular image formats on the Web, and because people often like to modify JPEG images, GDI+ provides the following transformations that can be performed on JPEG images without loss of information:
* Rotate 90 degrees
* Rotate 180 degrees
* Rotate 270 degrees
* Flip horizontally
* Flip vertically
You can apply one of the transformations shown in the preceding list when you call the
GdipSaveImageToFile. If the following conditions are met, then the transformation will proceed without loss of information:
* The file used to construct the
Image object is a JPEG file.
* The width and height of the image are both multiples of 16.
If the width and height of the image are not both multiples of 16,
GDI+ will do its best to preserve the image quality when you apply one of the rotation or flipping transformations shown in the preceding list.
To transform a JPEG image, initialize an
EncoderParameters structure and pass the address of that object to the
GdipSaveImageToFile function. Initialize the
EncoderParameters structure so that it has an array that consists of one
EncoderParameter structure.
Initialize that one
EncoderParameter structure so that its
Value member points to a DWORD variable that holds one of the following elements of the
EncoderValue enumeration:
* EncoderValueTransformRotate90,
* EncoderValueTransformRotate180,
* EncoderValueTransformRotate270,
* EncoderValueTransformFlipHorizontal,
* EncoderValueTransformFlipVertical
Set the
Guid member of the
EncoderParameter structure to
$EncoderTransformation.
The following console application creates an Image object from a JPEG file and then saves the image to a new file. During the save process, the image is rotated 90 degrees. If the width and height of the image are both multiples of 16, the process of rotating and saving the image causes no loss of information.
C++
#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;
}
PowerBASICThe main function relies on the helper function
GdiPlusGetEncoderClsid to retrieve the guid of the TIFF encoder.
#COMPILE EXE
#DIM ALL
#INCLUDE "GDIPLUS.INC"
#INCLUDE "GDIPUTILS.INC"
' ========================================================================================
' Main
' ========================================================================================
FUNCTION PBMAIN
LOCAL hStatus AS LONG
LOCAL token AS DWORD
LOCAL StartupInput AS GdiplusStartupInput
LOCAL EncoderClsid AS GUID
LOCAL pImage AS DWORD
LOCAL eps AS EncoderParameters
LOCAL ep AS EncoderParameter
LOCAL nWidth AS DWORD
LOCAL nHeight AS DWORD
LOCAL transformation AS DWORD
LOCAL strFileName AS STRING
' // Initialize GDI+
StartupInput.GdiplusVersion = 1
hStatus = GdiplusStartup(token, StartupInput, BYVAL %NULL)
IF hStatus THEN
PRINT "Error initializing GDI+"
EXIT FUNCTION
END IF
strFileName = UCODE$("Shapes.jpg")
hStatus = GdipLoadImageFromFile(STRPTR(strFileName), pImage)
' // Determine whether the width and height of the image are multiples of 16.
hStatus = GdipGetImageWidth(pImage, nWidth)
hStatus = GdipGetImageHeight(pImage, nHeight)
PRINT "The width of the image is" & STR$(nWidth)
IF (nWidth / 16 - nWidth \ 16) = 0 THEN
PRINT "which is a multiple of 16."
ELSE
PRINT "which is not a multiple of 16."
END IF
PRINT "The height of the image is" & STR$(nHeight)
IF (nHeight / 16 - nHeight \ 16) = 0 THEN
PRINT "which is a multiple of 16."
ELSE
PRINT "which is not a multiple of 16."
END IF
' // Get the CLSID of the JPEG encoder.
EncoderClsid = GUID$(GdiPlusGetEncoderClsid("image/jpeg"))
' // Before we call GdipSaveImageToFile, we must initialize an
' // EncoderParameters structure The EncoderParameters structure
' // has an array of EncoderParameter structures. In this
' // case, there is only one EncoderParameter structure in the array.
' // The one EncoderParameter structure has an array of values.
' // In this case, there is only one value (of type DWORD)
' // in the array. We will set that value to %EncoderValueTransformRotate90.
eps.count = 1
eps.Parameter(0).pGuid = $EncoderTransformation
eps.Parameter(0).dwType = %EncoderParameterValueTypeLong
eps.Parameter(0).NumberOfValues = 1
' // Rotate and save the image.
transformation = %EncoderValueTransformRotate90
eps.Parameter(0).Value = VARPTR(transformation)
strFileName = UCODE$("ShapesR90.jpg")
hStatus = GdipSaveImageToFile(pImage, STRPTR(strFileName), EncoderClsid, eps)
IF hStatus = %StatusOk THEN
PRINT "ShapesR90.jpg saved successfully"
ELSE
PRINT "Attempt to save ShapesR90.jpg failed"
END IF
' // Cleanup
IF pImage THEN GdipDisposeImage(pImage)
' // Shutdown GDI+
GdiplusShutdown token
WAITKEY$
END FUNCTION
' ========================================================================================