1// tImageJPG.cpp 
2// 
3// This class knows how to load and save a JPeg (.jpg and .jpeg) file. It does zero processing of image data. It knows 
4// the details of the jpg file format and loads the data into a tPixel array. These tPixels may be 'stolen' by the 
5// tPicture's constructor if a jpg file is specified. After the array is stolen the tImageJPG is invalid. This is 
6// purely for performance. The loading and saving uses libjpeg-turbo. See Licence_LibJpegTurbo.txt for more info. 
7// 
8// Copyright (c) 2020 Tristan Grimmer. 
9// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
10// granted, provided that the above copyright notice and this permission notice appear in all copies. 
11// 
12// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
13// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
14// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
15// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
16// PERFORMANCE OF THIS SOFTWARE. 
17 
18#include <System/tFile.h> 
19#include "Image/tImageJPG.h" 
20#if defined(PLATFORM_LINUX) 
21#include "TurboJpeg/Linux/turbojpeg.h" 
22#elif defined(PLATFORM_WINDOWS) 
23#include "TurboJpeg/Windows/turbojpeg.h" 
24#endif 
25 
26 
27using namespace tSystem; 
28namespace tImage 
29{ 
30 
31 
32bool tImageJPG::Load(const tString& jpgFile, bool strict) 
33{ 
34 Clear(); 
35 
36 if (tSystem::tGetFileType(jpgFile) != tSystem::tFileType::JPG) 
37 return false; 
38 
39 if (!tFileExists(jpgFile)) 
40 return false; 
41 
42 int numBytes = 0; 
43 uint8* jpgFileInMemory = tLoadFile(jpgFile, nullptr, &numBytes); 
44 bool success = Set(jpgFileInMemory, numBytes, strict); 
45 delete[] jpgFileInMemory; 
46 
47 return success; 
48} 
49 
50 
51bool tImageJPG::Set(const uint8* jpgFileInMemory, int numBytes, bool strict) 
52{ 
53 Clear(); 
54 if ((numBytes <= 0) || !jpgFileInMemory) 
55 return false; 
56 
57 tjhandle tjInstance = tjInitDecompress(); 
58 if (!tjInstance) 
59 return false; 
60 
61 int subSamp = 0; 
62 int colourSpace = 0; 
63 int headerResult = tjDecompressHeader3(tjInstance, jpgFileInMemory, numBytes, &Width, &Height, &subSamp, &colourSpace); 
64 if (headerResult < 0) 
65 return false; 
66 
67 int numPixels = Width * Height; 
68 Pixels = new tPixel[numPixels]; 
69 
70 int jpgPixelFormat = TJPF_RGBA; 
71 int flags = 0; 
72 flags |= TJFLAG_BOTTOMUP; 
73 //flags |= TJFLAG_FASTUPSAMPLE; 
74 //flags |= TJFLAG_FASTDCT; 
75 flags |= TJFLAG_ACCURATEDCT; 
76 
77 int decomResult = tjDecompress2 
78 ( 
79 tjInstance, jpgFileInMemory, numBytes, (uint8*)Pixels, 
80 Width, 0, Height, jpgPixelFormat, flags 
81 ); 
82 
83 bool abortLoad = false; 
84 if (decomResult < 0) 
85 { 
86 int errorSeverity = tjGetErrorCode(tjInstance); 
87 char* errorMsg = tjGetErrorStr2(tjInstance); 
88 switch (errorSeverity) 
89 { 
90 case TJERR_WARNING: 
91 tPrintf("Warning: JPG ill-formed: %s\n", errorMsg); 
92 if (strict) 
93 abortLoad = true; 
94 break; 
95 
96 case TJERR_FATAL: 
97 tPrintf("Error: JPG fatally ill-formed: %s\n", errorMsg); 
98 abortLoad = true; 
99 break; 
100 } 
101 } 
102 
103 tjDestroy(tjInstance); 
104 if (abortLoad) 
105 { 
106 Clear(); 
107 return false; 
108 } 
109 
110 SrcPixelFormat = tPixelFormat::R8G8B8; 
111 return true; 
112} 
113 
114 
115bool tImageJPG::Set(tPixel* pixels, int width, int height, bool steal) 
116{ 
117 Clear(); 
118 if (!pixels || (width <= 0) || (height <= 0)) 
119 return false; 
120 
121 Width = width; 
122 Height = height; 
123 if (steal) 
124 { 
125 Pixels = pixels; 
126 } 
127 else 
128 { 
129 Pixels = new tPixel[Width*Height]; 
130 tStd::tMemcpy(Pixels, pixels, Width*Height*sizeof(tPixel)); 
131 } 
132 
133 SrcPixelFormat = tPixelFormat::R8G8B8A8; 
134 return true; 
135} 
136 
137 
138bool tImageJPG::Save(const tString& jpgFile, int quality) const 
139{ 
140 if (!IsValid()) 
141 return false; 
142 
143 if (tSystem::tGetFileType(jpgFile) != tSystem::tFileType::JPG) 
144 return false; 
145 
146 tjhandle tjInstance = tjInitCompress(); 
147 if (!tjInstance) 
148 return false; 
149 
150 uint8* jpegBuf = nullptr; 
151 ulong jpegSize = 0; 
152 
153 int flags = 0; 
154 flags |= TJFLAG_BOTTOMUP; 
155 //flags |= TJFLAG_FASTUPSAMPLE; 
156 //flags |= TJFLAG_FASTDCT; 
157 flags |= TJFLAG_ACCURATEDCT; 
158 
159 int compResult = tjCompress2(tjInstance, (uint8*)Pixels, Width, 0, Height, TJPF_RGBA, 
160 &jpegBuf, &jpegSize, TJSAMP_444, quality, flags); 
161 
162 tjDestroy(tjInstance); 
163 if (compResult < 0) 
164 { 
165 tjFree(jpegBuf); 
166 return false; 
167 } 
168 
169 tFileHandle fileHandle = tOpenFile(jpgFile.Chars(), "wb"); 
170 if (!fileHandle) 
171 { 
172 tjFree(jpegBuf); 
173 return false; 
174 } 
175 bool success = tWriteFile(fileHandle, jpegBuf, jpegSize); 
176 tCloseFile(fileHandle); 
177 tjFree(jpegBuf); 
178 
179 return success; 
180} 
181 
182 
183tPixel* tImageJPG::StealPixels() 
184{ 
185 tPixel* pixels = Pixels; 
186 Pixels = nullptr; 
187 Width = 0; 
188 Height = 0; 
189 return pixels; 
190} 
191 
192 
193} 
194