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 |   |
27 | using namespace tSystem;  |
28 | namespace tImage  |
29 | {  |
30 |   |
31 |   |
32 | bool 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 |   |
51 | bool 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 = 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 |   |
115 | bool 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 |   |
138 | bool 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 |   |
183 | tPixel* tImageJPG::StealPixels()  |
184 | {  |
185 | tPixel* pixels = Pixels;  |
186 | Pixels = nullptr;  |
187 | Width = 0;  |
188 | Height = 0;  |
189 | return pixels;  |
190 | }  |
191 |   |
192 |   |
193 | }  |
194 | |