1 /*
2  * steghide 0.5.1 - a steganography program
3  * Copyright (C) 1999-2003 Stefan Hetzl <shetzl@chello.at>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  */
20 
21 #include "common.h"
22 
23 #ifdef USE_LIBJPEG
24 
25 #include <cstdio>
26 #include <iostream>
27 
28 #include "AUtils.h"
29 #include "BinaryIO.h"
30 #include "JpegFile.h"
31 #include "JpegSampleValue.h"
32 #include "SMDConstructionHeuristic.h"
33 #include "error.h"
34 
JpegFile(BinaryIO * io)35 JpegFile::JpegFile (BinaryIO* io)
36 	: CvrStgFile()
37 {
38 	setSamplesPerVertex (SamplesPerVertex) ;
39 	setRadius (Radius) ;
40 	setEmbValueModulus (EmbValueModulus) ;
41 	HeightInBlocks = NULL ;
42 	WidthInBlocks = NULL ;
43 	read (io) ;
44 }
45 
~JpegFile()46 JpegFile::~JpegFile ()
47 {
48 	if (WidthInBlocks) {
49 		delete[] WidthInBlocks ;
50 	}
51 	if (HeightInBlocks) {
52 		delete[] HeightInBlocks ;
53 	}
54 }
55 
getProperties() const56 std::list<CvrStgFile::Property> JpegFile::getProperties () const
57 {
58 	std::list<CvrStgFile::Property> retval ;
59 
60 	// format
61 	retval.push_back (CvrStgFile::Property (_("format"), "jpeg")) ;
62 
63 	return retval ;
64 }
65 
read(BinaryIO * io)66 void JpegFile::read (BinaryIO* io)
67 {
68 	CvrStgFile::read (io) ;
69 
70 	// TODO - use BinaryIO (or similar class) as input/output module for libjpeg (to avoid the NotImplementedError and using FILE* here)
71 	FILE *infile = NULL ;
72 	if (io->is_std()) {
73 		throw NotImplementedError (_("can not use standard input as source for jpeg files with libjpeg.")) ;
74 	}
75 	else {
76 		infile = io->getStream() ;
77 		rewind (infile) ;
78 	}
79 
80 	struct jpeg_error_mgr errmgr ;
81 	DeCInfo.err = jpeg_std_error (&errmgr) ;
82 	jpeg_create_decompress (&DeCInfo) ;
83 	jpeg_stdio_src (&DeCInfo, infile) ;
84 	jpeg_read_header (&DeCInfo, TRUE) ;
85 
86 	DctCoeffs = jpeg_read_coefficients (&DeCInfo) ;
87 
88 	// fill HeightInBlocks and WidthInBlocks
89 	unsigned short max_v_samp_factor = 0 ;
90 	unsigned short max_h_samp_factor = 0 ;
91 	for (unsigned short icomp = 0 ; icomp < DeCInfo.num_components ; icomp++) {
92 		max_v_samp_factor = AUtils::max<unsigned short> (max_v_samp_factor, DeCInfo.comp_info[icomp].v_samp_factor) ;
93 		max_h_samp_factor = AUtils::max<unsigned short> (max_h_samp_factor, DeCInfo.comp_info[icomp].h_samp_factor) ;
94 	}
95 	HeightInBlocks = new unsigned int[DeCInfo.num_components] ;
96 	WidthInBlocks = new unsigned int[DeCInfo.num_components] ;
97 	for (unsigned short icomp = 0 ; icomp < DeCInfo.num_components ; icomp++) {
98 		HeightInBlocks[icomp] = AUtils::div_roundup<unsigned int> (DeCInfo.image_height * DeCInfo.comp_info[icomp].v_samp_factor,
99 																	8 * max_v_samp_factor) ;
100 		WidthInBlocks[icomp] = AUtils::div_roundup<unsigned int> (DeCInfo.image_width * DeCInfo.comp_info[icomp].h_samp_factor,
101 																	8 * max_h_samp_factor) ;
102 	}
103 
104 	// resize LinDctCoeffs to size that is enough to contain all dct coeffs
105 	unsigned long totalnumcoeffs = 0 ;
106 	for (unsigned short icomp = 0 ; icomp < DeCInfo.num_components ; icomp++) {
107 		totalnumcoeffs += CoeffPerBlock * (HeightInBlocks[icomp] * WidthInBlocks[icomp]) ;
108 	}
109 	LinDctCoeffs.resize (totalnumcoeffs) ;
110 
111 	// read data from jpeglib's virtual array into LinDctCoeffs and StegoIndices
112 	UWORD32 linindex = 0 ;
113 	for (unsigned short icomp = 0 ; icomp < DeCInfo.num_components ; icomp++) {
114 		unsigned int currow = 0 ;
115 		while (currow < HeightInBlocks[icomp]) {
116 			unsigned int naccess = 1 ;
117 			JBLOCKARRAY array = (*(DeCInfo.mem->access_virt_barray))
118 				((j_common_ptr) &DeCInfo, DctCoeffs[icomp], currow, naccess, FALSE) ;
119 			for (unsigned int irow = 0 ; irow < naccess ; irow++) {
120 				for (unsigned int iblock = 0 ; iblock < WidthInBlocks[icomp] ; iblock++) {
121 					for (unsigned int icoeff = 0 ; icoeff < CoeffPerBlock ; icoeff++) {
122 						LinDctCoeffs[linindex] = array[irow][iblock][icoeff] ;
123 
124 						// don't use zero dct coefficients to embed data
125 						if (LinDctCoeffs[linindex] != 0) {
126 							StegoIndices.push_back (linindex) ;
127 						}
128 						linindex++ ;
129 					}
130 				}
131 			}
132 			currow += naccess ;
133 		}
134 	}
135 }
136 
write()137 void JpegFile::write ()
138 {
139 	CvrStgFile::write() ;
140 
141 	FILE* outfile = getBinIO()->getStream() ;
142 
143 	// prepare for writing
144 	jpeg_create_compress (&CInfo) ;
145 	jpeg_copy_critical_parameters (&DeCInfo, &CInfo) ;
146 	struct jpeg_error_mgr jerr2 ;
147 	CInfo.err = jpeg_std_error(&jerr2) ;
148 	jpeg_stdio_dest (&CInfo, outfile) ;
149 
150 	// write file header
151 	jpeg_write_coefficients (&CInfo, DctCoeffs) ;
152 
153 	UWORD32 linindex = 0 ;
154 	for (unsigned short icomp = 0 ; icomp < CInfo.num_components ; icomp++) {
155 
156 		unsigned int currow = 0 ;
157 		while (currow < HeightInBlocks[icomp]) {
158 			unsigned int naccess = 1 ;
159 			JBLOCKARRAY array = (*(CInfo.mem->access_virt_barray))
160 				((j_common_ptr) &CInfo, DctCoeffs[icomp], currow, naccess, TRUE) ;
161 			for (unsigned int irow = 0 ; irow < naccess ; irow++) {
162 				for (unsigned int iblock = 0 ; iblock < WidthInBlocks[icomp] ; iblock++) {
163 					for (unsigned int icoeff = 0 ; icoeff < CoeffPerBlock ; icoeff++) {
164 						array[irow][iblock][icoeff] = LinDctCoeffs[linindex] ;
165 						linindex++ ;
166 					}
167 				}
168 			}
169 			currow += naccess ;
170 		}
171 	}
172 
173 	// write and deallocate everything (writing is possible only once)
174 	jpeg_finish_compress (&CInfo) ;
175 	jpeg_destroy_compress(&CInfo);
176 	jpeg_finish_decompress (&DeCInfo) ;
177 	jpeg_destroy_decompress(&DeCInfo);
178 }
179 
getNumSamples(void) const180 unsigned long JpegFile::getNumSamples (void) const
181 {
182 	return StegoIndices.size() ;
183 }
184 
getSampleValue(const SamplePos pos) const185 SampleValue* JpegFile::getSampleValue (const SamplePos pos) const
186 {
187 	myassert (pos < StegoIndices.size()) ;
188 	return new JpegSampleValue (LinDctCoeffs[StegoIndices[pos]]) ;
189 }
190 
replaceSample(const SamplePos pos,const SampleValue * s)191 void JpegFile::replaceSample (const SamplePos pos, const SampleValue* s)
192 {
193 	const JpegSampleValue* sample = dynamic_cast<const JpegSampleValue*> (s) ;
194 	myassert (sample != NULL) ;
195 	myassert (pos <= StegoIndices.size()) ;
196 	LinDctCoeffs[StegoIndices[pos]] = sample->getDctCoeff() ;
197 }
198 
getEmbeddedValue(const SamplePos pos) const199 EmbValue JpegFile::getEmbeddedValue (const SamplePos pos) const
200 {
201 	myassert (pos < StegoIndices.size()) ;
202 	return JpegSampleValue::calcEValue (LinDctCoeffs[StegoIndices[pos]]) ;
203 }
204 
getMatchingAlgorithms(Graph * g,Matching * m) const205 std::vector<MatchingAlgorithm*> JpegFile::getMatchingAlgorithms (Graph* g, Matching* m) const
206 {
207 	std::vector<MatchingAlgorithm*> retval ;
208 	retval.push_back (new SMDConstructionHeuristic (g, m)) ;
209 	return retval ;
210 }
211 
212 #ifdef DEBUG
getFrequencies()213 std::map<SampleKey,unsigned long>* JpegFile::getFrequencies ()
214 {
215 	unsigned long n = LinDctCoeffs.size() ;
216 	std::map<SampleKey,unsigned long>* table = new std::map<SampleKey,unsigned long> () ;
217 
218 	for (unsigned long pos = 0 ; pos < n ; pos++) {
219 		SampleValue *sv = (SampleValue*) new JpegSampleValue (LinDctCoeffs[pos]) ;
220 		(*table)[sv->getKey()]++ ;
221 		delete sv ;
222 	}
223 
224 	return table ;
225 }
226 
printFrequencies(const std::map<SampleKey,unsigned long> & freqs)227 void JpegFile::printFrequencies (const std::map<SampleKey,unsigned long>& freqs)
228 {
229 	std::list<std::string> output ;
230 
231 	// insert the positive dct coeffs into output list
232 	for (std::map<SampleKey,unsigned long>::const_iterator pit = freqs.begin() ; pit->first < 2147483648UL /* 2^31 */ ; pit++) {
233 		char buf[30] ;
234 		sprintf (buf, "%ld: %lu", (long) pit->first, pit->second) ;
235 		output.push_back (std::string(buf)) ;
236 	}
237 
238 	// insert the negative dct coeffs into output list
239 	for (std::map<SampleKey,unsigned long>::const_reverse_iterator nit = freqs.rbegin() ; nit->first > 2147483648UL /* 2^31 */ ; nit++) {
240 		char buf[30] ;
241 		sprintf (buf, "%ld: %lu", (long) nit->first, nit->second) ;
242 		output.push_front (std::string(buf)) ;
243 	}
244 
245 	for (std::list<std::string>::const_iterator it = output.begin() ; it != output.end() ; it++) {
246 		std::cout << *it << std::endl ;
247 	}
248 }
249 #endif // def DEBUG
250 
251 #endif // def USE_LIBJPEG
252