|
1
|
1 #include "stdafx.h"
|
|
|
2 #include "ImageEncoder.h"
|
|
|
3 #include "gdiplus_helpers.h"
|
|
|
4 #include <memory>
|
|
|
5 #include <vector>
|
|
|
6
|
|
|
7 using namespace Gdiplus;
|
|
|
8 static GdiplusErrorHandler EH;
|
|
|
9
|
|
|
10 int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
|
|
|
11 {
|
|
|
12 UINT num = 0; // number of image encoders
|
|
|
13 UINT size = 0; // size of the image encoder array in bytes
|
|
|
14
|
|
|
15 ImageCodecInfo* pImageCodecInfo = NULL;
|
|
|
16
|
|
|
17 GetImageEncodersSize(&num, &size);
|
|
|
18 if(size == 0)
|
|
|
19 return -1; // Failure
|
|
|
20
|
|
|
21 pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
|
|
|
22 if(pImageCodecInfo == NULL)
|
|
|
23 return -1; // Failure
|
|
|
24
|
|
|
25 GetImageEncoders(num, size, pImageCodecInfo);
|
|
|
26
|
|
|
27 for(UINT j = 0; j < num; ++j)
|
|
|
28 {
|
|
|
29 if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
|
|
|
30 {
|
|
|
31 *pClsid = pImageCodecInfo[j].Clsid;
|
|
|
32 free(pImageCodecInfo);
|
|
|
33 return j; // Success
|
|
|
34 }
|
|
|
35 }
|
|
|
36
|
|
|
37 free(pImageCodecInfo);
|
|
|
38 return -1; // Failure
|
|
|
39 }
|
|
|
40
|
|
|
41 void ConvertImage(const TCHAR * in, const TCHAR * out, const TCHAR * format, ULONG quality) {
|
|
|
42 GdiplusScope scope;
|
|
|
43 Bitmap image (in);
|
|
|
44 EH << image.GetLastStatus();
|
|
|
45 SaveImage(&image, out, format, quality);
|
|
|
46 }
|
|
|
47
|
|
|
48 static void add16clip(uint16_t& v, int d) {
|
|
|
49 int v2 = (int) v + d;
|
|
|
50 if ( v2 < 0 ) v2 = 0;
|
|
|
51 if ( v2 > (int) UINT16_MAX ) v2 = UINT16_MAX;
|
|
|
52 v = (uint16_t) v2;
|
|
|
53 }
|
|
|
54
|
|
|
55 std::unique_ptr<Gdiplus::Bitmap> image16bpcto8(Gdiplus::Bitmap* img) {
|
|
|
56
|
|
|
57 const unsigned channels = [img] {
|
|
|
58 switch (img->GetPixelFormat()) {
|
|
|
59 case PixelFormat48bppRGB:
|
|
|
60 return 3;
|
|
|
61 case PixelFormat16bppGrayScale:
|
|
|
62 return 1;
|
|
|
63 default:
|
|
|
64 throw std::runtime_error("Invalid pixel format");
|
|
|
65 }
|
|
|
66 } ();
|
|
|
67
|
|
|
68
|
|
|
69 const unsigned Width = img->GetWidth();
|
|
|
70 const unsigned Height = img->GetHeight();
|
|
|
71
|
|
|
72 if ( Width == 0 || Height == 0 ) throw std::runtime_error("Invalid dimensions");
|
|
|
73
|
|
|
74 std::unique_ptr< Gdiplus::Bitmap > ret ( new Gdiplus::Bitmap( Width, Height, channels == 3 ? PixelFormat24bppRGB : PixelFormat8bppIndexed ) );
|
|
|
75
|
|
|
76
|
|
|
77 Gdiplus::Rect rcWhole = {};
|
|
|
78 rcWhole.Width = Width; rcWhole.Height = Height;
|
|
|
79 Gdiplus::BitmapData dataIn, dataOut;
|
|
|
80 EH << ret->LockBits( &rcWhole, ImageLockModeWrite, PixelFormat24bppRGB, &dataOut);
|
|
|
81 EH << img->LockBits(&rcWhole, ImageLockModeRead, PixelFormat48bppRGB, &dataIn);
|
|
|
82
|
|
|
83
|
|
|
84
|
|
|
85
|
|
|
86 const uint8_t * inLine = (const uint8_t*)dataIn.Scan0;
|
|
|
87 uint8_t * outLine = (uint8_t*)dataOut.Scan0;
|
|
|
88
|
|
|
89 std::vector<uint16_t> curLineBuf, nextLineBuf;
|
|
|
90 curLineBuf.resize( (Width + 2) * channels );
|
|
|
91 nextLineBuf.resize( (Width + 2) * channels );
|
|
|
92
|
|
|
93 memcpy(&nextLineBuf[channels], inLine, Width * channels * 2 );
|
|
|
94
|
|
|
95 for (unsigned y = 0; y < Height; ++y) {
|
|
|
96 std::swap( curLineBuf, nextLineBuf );
|
|
|
97 if (y + 1 < Height) {
|
|
|
98 inLine += dataIn.Stride;
|
|
|
99 memcpy(&nextLineBuf[channels], inLine, Width * channels * 2 );
|
|
|
100 }
|
|
|
101
|
|
|
102 auto & inPix = curLineBuf;
|
|
|
103 auto & inPixNext = nextLineBuf;
|
|
|
104 size_t inOffset = channels;
|
|
|
105 uint8_t * outPix = outLine;
|
|
|
106 for (unsigned x = 0; x < Width; ++x) {
|
|
|
107 for (int c = 0; c < (int)channels; ++c) {
|
|
|
108 uint16_t orig = inPix[inOffset];
|
|
|
109 uint16_t v8 = orig >> 8;
|
|
|
110 uint16_t v16 = v8 | (v8 << 8);
|
|
|
111
|
|
|
112 outPix[c] = (uint8_t) v8;
|
|
|
113
|
|
|
114 int d = (int) orig - (int) v16;
|
|
|
115
|
|
|
116 add16clip(inPix [inOffset + channels], d * 7 / 16 ); // x+1
|
|
|
117 add16clip(inPixNext[inOffset - channels], d * 3 / 16 ); // x-1, y+1
|
|
|
118 add16clip(inPixNext[inOffset ], d * 5 / 16 ); // y+1
|
|
|
119 add16clip(inPixNext[inOffset + channels], d * 1 / 16 ); // x+1, y+1
|
|
|
120
|
|
|
121 ++ inOffset;
|
|
|
122 }
|
|
|
123
|
|
|
124 outPix += channels;
|
|
|
125 }
|
|
|
126
|
|
|
127 outLine += dataOut.Stride;
|
|
|
128 }
|
|
|
129
|
|
|
130 EH << img->UnlockBits(&dataIn);
|
|
|
131 EH << ret->UnlockBits(&dataOut);
|
|
|
132
|
|
|
133 return ret;
|
|
|
134 }
|
|
|
135
|
|
|
136 void SaveImage(Gdiplus::Image* bmp, const TCHAR* out, const TCHAR* format, ULONG quality) {
|
|
|
137 if (bmp->GetType() != Gdiplus::ImageTypeBitmap) throw std::runtime_error("Bitmap expected");
|
|
|
138 SaveImage(static_cast<Gdiplus::Bitmap*>(bmp), out, format, quality);
|
|
|
139 }
|
|
|
140 void SaveImage(Gdiplus::Bitmap* image, const TCHAR* out, const TCHAR* format, ULONG quality) {
|
|
|
141 CLSID encoderClsid;
|
|
|
142 EncoderParameters encoderParameters;
|
|
|
143
|
|
|
144 if (GetEncoderClsid(format, &encoderClsid) < 0) throw std::runtime_error("Encoder not found");
|
|
|
145
|
|
|
146 if (_tcscmp(format, _T("image/jpeg")) == 0) {
|
|
|
147 encoderParameters.Count = 1;
|
|
|
148 encoderParameters.Parameter[0].Guid = EncoderQuality;
|
|
|
149 encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
|
|
|
150 encoderParameters.Parameter[0].NumberOfValues = 1;
|
|
|
151 encoderParameters.Parameter[0].Value = &quality;
|
|
|
152
|
|
|
153 EH << image->Save(out, &encoderClsid, &encoderParameters);
|
|
|
154 } else {
|
|
|
155 EH << image->Save(out, &encoderClsid, NULL);
|
|
|
156 }
|
|
|
157 }
|