|
1 /* |
|
2 * Copyright (c) 2010 Ixonos Plc. |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - Initial contribution |
|
11 * |
|
12 * Contributors: |
|
13 * Ixonos Plc |
|
14 * |
|
15 * Description: |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 #include "CJpegScale.h" |
|
21 #include "CJpeg.h" |
|
22 #include "CJpegSave.h" |
|
23 #include "CExifParser.h" |
|
24 |
|
25 |
|
26 CJpegScale::CJpegScale( MJpegScaleCallBack* aCallBack ) |
|
27 : iTargetScale( 0 ) |
|
28 , iQuality( 95 ) |
|
29 , iCallBack( aCallBack ) |
|
30 { |
|
31 } |
|
32 |
|
33 |
|
34 |
|
35 CJpegScale::~CJpegScale() |
|
36 { |
|
37 } |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 void CJpegScale::ScaleL() |
|
43 { |
|
44 const TInt KxShift = 14; |
|
45 const TInt KxMul = ( 1 << KxShift ); |
|
46 const TInt KxAnd = KxMul - 1; |
|
47 |
|
48 CJpeg* source; |
|
49 CJpegSave* target; |
|
50 |
|
51 // file server session |
|
52 RFs fs; |
|
53 User::LeaveIfError( fs.Connect() ); |
|
54 CleanupClosePushL( fs ); |
|
55 |
|
56 // encoded file |
|
57 RFile targetFile; |
|
58 User::LeaveIfError( targetFile.Replace( fs, iTargetName, EFileWrite ) ); |
|
59 CleanupClosePushL( targetFile ); |
|
60 |
|
61 // jpeg source |
|
62 source = CJpeg::NewL(); |
|
63 CleanupStack::PushL( source ); |
|
64 |
|
65 // jpeg target |
|
66 target = CJpegSave::NewL( &fs, &targetFile ); |
|
67 CleanupStack::PushL( target ); |
|
68 |
|
69 source->OpenL( iSourceName ); |
|
70 |
|
71 TJpegData info = source->Info(); |
|
72 |
|
73 TInt tw = iTargetSize.iWidth; |
|
74 TInt th = iTargetSize.iHeight; |
|
75 |
|
76 TInt sw = info.iSize.iWidth; |
|
77 TInt sh = info.iSize.iHeight; |
|
78 |
|
79 TInt xscale; |
|
80 TInt yscale; |
|
81 |
|
82 // |
|
83 // Decide scale or target size |
|
84 // |
|
85 if( iTargetScale != 0 ) |
|
86 { |
|
87 xscale = (TInt)(iTargetScale * KxMul); |
|
88 yscale = xscale; |
|
89 tw = xscale * sw / KxMul; |
|
90 th = yscale * sh / KxMul; |
|
91 } |
|
92 else |
|
93 { |
|
94 yscale = KxMul * th / sh; |
|
95 xscale = KxMul * tw / sw; |
|
96 } |
|
97 |
|
98 if( tw>sw || th>sh ) |
|
99 { |
|
100 // no upscaling yet. |
|
101 User::Leave( KErrNotSupported ); |
|
102 } |
|
103 |
|
104 // |
|
105 // Hardscale is scaling which is done in jpeg decoder |
|
106 // possible hardscales are 1:1, 1:2, 1:4 and 1:8 |
|
107 // hard scaling speeds up things a lot. |
|
108 // |
|
109 TInt hardScale = 1; |
|
110 source->SetScale( EScale1 ); |
|
111 |
|
112 if( xscale <= KxMul/8 && yscale <= KxMul/8 ) |
|
113 { |
|
114 hardScale = 8; |
|
115 source->SetScale( EScale8 ); |
|
116 } |
|
117 |
|
118 else if( xscale <= KxMul/4 && yscale <= KxMul/4 ) |
|
119 { |
|
120 hardScale = 4; |
|
121 source->SetScale( EScale4 ); |
|
122 } |
|
123 |
|
124 else if( xscale <= KxMul/2 && yscale <= KxMul/2 ) |
|
125 { |
|
126 hardScale = 2; |
|
127 source->SetScale( EScale2 ); |
|
128 } |
|
129 // else hardscale = 1 |
|
130 |
|
131 |
|
132 sw /= hardScale; |
|
133 sh /= hardScale; |
|
134 xscale *= hardScale; |
|
135 yscale *= hardScale; |
|
136 |
|
137 |
|
138 // |
|
139 // Jpeg store bitmap block |
|
140 // Jpeg encoder always uses block size of 16x8 pixels |
|
141 // |
|
142 TBitmapHandle blk; |
|
143 blk.iSize = TSize( 16,8 ); |
|
144 blk.iData = new( ELeave )TUint32[ 16*8 ]; |
|
145 CleanupStack::PushL( blk.iData ); |
|
146 |
|
147 iTargetSize = TSize( tw,th ); |
|
148 |
|
149 CExifParser* parser = CExifParser::NewLC(); |
|
150 parser->ParseL(source->ExifData()); |
|
151 |
|
152 // ImageWidth - Not used for JPEG images |
|
153 if (parser->TagExist(CExifParser::EIfd0, 0x0100)) |
|
154 { |
|
155 parser->DeleteTag ( CExifParser::EIfd0, 0x0100); |
|
156 //iExifParser->AddTagL ( CExifParser::EIfd0, 0x0100, (TUint16)iTargetSize.iWidth); |
|
157 } |
|
158 |
|
159 // ImageHeight - Not used for JPEG images |
|
160 if (parser->TagExist(CExifParser::EIfd0, 0x0101)) |
|
161 { |
|
162 parser->DeleteTag ( CExifParser::EIfd0, 0x0101); |
|
163 //iExifParser->AddTagL ( CExifParser::EIfd0, 0x0101, (TUint16)iTargetSize.iHeight); |
|
164 } |
|
165 |
|
166 // PixelXResolution |
|
167 if (parser->TagExist(CExifParser::ESubIfd, 0xA002)) |
|
168 { |
|
169 parser->DeleteTag ( CExifParser::ESubIfd, 0xA002); |
|
170 parser->AddTagL ( CExifParser::ESubIfd, 0xA002,(TUint32)iTargetSize.iWidth); |
|
171 } |
|
172 // PixelYResolution |
|
173 if (parser->TagExist(CExifParser::ESubIfd, 0xA003)) |
|
174 { |
|
175 parser->DeleteTag ( CExifParser::ESubIfd, 0xA003); |
|
176 parser->AddTagL ( CExifParser::ESubIfd, 0xA003, (TUint32)iTargetSize.iHeight); |
|
177 } |
|
178 |
|
179 TPtr8 exif = parser->SaveL(parser->ThumbData()); |
|
180 |
|
181 // |
|
182 // Initialize jpeg encoder |
|
183 // no exif |
|
184 // save buffer ~16KB |
|
185 // use iQuality as quality ( values from 0 to 100 ) |
|
186 // |
|
187 target->StartSaveL( iTargetSize, exif, 16000, iQuality ); |
|
188 |
|
189 CleanupStack::PopAndDestroy(); // parser |
|
190 |
|
191 TInt yStep = 8; // jpeg encoder block height |
|
192 TInt yPos = 0; // number of encoded lines |
|
193 |
|
194 // |
|
195 // Reserve scaled buffer |
|
196 // |
|
197 TInt pixels = tw * ( yStep*2 ); // room for 2 block lines |
|
198 |
|
199 TUint32* cr = new( ELeave )TUint32[ pixels ]; |
|
200 CleanupStack::PushL( cr ); |
|
201 TUint32* cg = new( ELeave )TUint32[ pixels ]; |
|
202 CleanupStack::PushL( cg ); |
|
203 TUint32* cb = new( ELeave )TUint32[ pixels ]; |
|
204 CleanupStack::PushL( cb ); |
|
205 TUint32* cm = new( ELeave )TUint32[ pixels ]; |
|
206 CleanupStack::PushL( cm ); |
|
207 |
|
208 // |
|
209 // Clear scaled buffer |
|
210 // |
|
211 Mem::FillZ( cr, pixels * sizeof( TUint32 ) ); |
|
212 Mem::FillZ( cg, pixels * sizeof( TUint32 ) ); |
|
213 Mem::FillZ( cb, pixels * sizeof( TUint32 ) ); |
|
214 Mem::FillZ( cm, pixels * sizeof( TUint32 ) ); |
|
215 |
|
216 |
|
217 TInt srcBlockHeight = info.iBlockSize.iHeight; |
|
218 |
|
219 TInt srcY1 = 0; |
|
220 TInt srcY2 = 0; |
|
221 TBitmapHandle bm; |
|
222 bm.iData = 0; |
|
223 |
|
224 for( TInt y=0; y<sh; y++ ) |
|
225 { |
|
226 if( iCallBack ) |
|
227 { |
|
228 iCallBack->JpegStatusCallBack( 100 * y / sh ); |
|
229 } |
|
230 // |
|
231 // Fill source buffer when needed |
|
232 // |
|
233 if( srcY2 <= y ) |
|
234 { |
|
235 delete bm.iData; |
|
236 TRect rect( 0,y*hardScale, info.iSize.iWidth, ( y + srcBlockHeight ) * hardScale ); |
|
237 bm = source->LoadImageL( rect ); |
|
238 srcY1 = y; |
|
239 srcY2 = srcY1 + bm.iSize.iHeight; |
|
240 } |
|
241 |
|
242 TUint32* srcRgb = (TUint32*)bm.iData; |
|
243 srcRgb += (y-srcY1) * bm.iSize.iWidth; |
|
244 |
|
245 TInt ty = y * yscale; |
|
246 TInt y2 = ty & KxAnd; |
|
247 TInt y1 = KxMul - y2; |
|
248 ty >>= KxShift; |
|
249 |
|
250 TInt oy = ( ty - yPos ) * tw; |
|
251 TUint32* pr = cr + oy; |
|
252 TUint32* pg = cg + oy; |
|
253 TUint32* pb = cb + oy; |
|
254 TUint32* pm = cm + oy; |
|
255 |
|
256 // |
|
257 // Add pixels to scaled buffer |
|
258 // |
|
259 for( TInt x=0; x<sw; x++ ) |
|
260 { |
|
261 TInt tx = x * xscale; |
|
262 TInt x2 = tx & KxAnd; |
|
263 TInt x1 = KxMul - x2; |
|
264 tx >>= KxShift; |
|
265 |
|
266 TUint32 pixel = *srcRgb++; |
|
267 |
|
268 TUint32 m1 = x1*y1 / KxMul; |
|
269 TUint32 m2 = x2*y1 / KxMul; |
|
270 TUint32 m3 = x1*y2 / KxMul; |
|
271 TUint32 m4 = x2*y2 / KxMul; |
|
272 |
|
273 TInt red = pixel >> 16; |
|
274 pr[ tx ] += m1 * red; |
|
275 pr[ tx+1 ] += m2 * red; |
|
276 pr[ tx+tw ] += m3 * red; |
|
277 pr[ tx+tw+1 ] += m4 * red; |
|
278 |
|
279 TInt green = ( pixel >> 8 ) & 255; |
|
280 pg[ tx ] += m1 * green; |
|
281 pg[ tx+1 ] += m2 * green; |
|
282 pg[ tx+tw ] += m3 * green; |
|
283 pg[ tx+tw+1 ] += m4 * green; |
|
284 |
|
285 TInt blue = pixel & 255; |
|
286 pb[ tx ] += m1 * blue; |
|
287 pb[ tx+1 ] += m2 * blue; |
|
288 pb[ tx+tw ] += m3 * blue; |
|
289 pb[ tx+tw+1 ] += m4 * blue; |
|
290 |
|
291 pm[ tx ] += m1; |
|
292 pm[ tx+1 ] += m2; |
|
293 pm[ tx+tw ] += m3; |
|
294 pm[ tx+tw+1 ] += m4; |
|
295 } |
|
296 |
|
297 |
|
298 // |
|
299 // Transfer scaled buffer block-by-block to jpeg encoder |
|
300 // |
|
301 if( ty >= yPos + yStep || y == sh-1 ) |
|
302 { |
|
303 yPos += yStep; |
|
304 |
|
305 TUint32* trgb = (TUint32*)blk.iData; |
|
306 |
|
307 for( TInt b=0; b<tw; b += 16 ) |
|
308 { |
|
309 TUint32* p = trgb; |
|
310 for( TInt by=0; by<8; by++ ) |
|
311 for( TInt bx=0; bx<16; bx++ ) |
|
312 { |
|
313 TUint32 pos = b + bx + by*tw; |
|
314 TUint32 bcr = cr[ pos ]; |
|
315 TUint32 bcg = cg[ pos ]; |
|
316 TUint32 bcb = cb[ pos ]; |
|
317 TUint32 m = cm[ pos ]; |
|
318 if( m ) |
|
319 { |
|
320 bcr /= m; |
|
321 bcg /= m; |
|
322 bcb /= m; |
|
323 } |
|
324 //trgb[ bx + by * 16 ] = bcr*65536 + bcg*256 + bcb; |
|
325 |
|
326 TUint32 c = bcr << 16; |
|
327 c += ( bcg << 8 ); |
|
328 c += bcb; |
|
329 *p++ = c; |
|
330 } |
|
331 target->SaveBlock( blk ); |
|
332 } |
|
333 |
|
334 // |
|
335 // move overflow line to first line in scaled buffer |
|
336 // |
|
337 TInt offset = tw * yStep; |
|
338 for( TInt rx=0; rx<tw; rx++ ) |
|
339 { |
|
340 cr[ rx ] = cr[ rx + offset ]; |
|
341 cg[ rx ] = cg[ rx + offset ]; |
|
342 cb[ rx ] = cb[ rx + offset ]; |
|
343 cm[ rx ] = cm[ rx + offset ]; |
|
344 |
|
345 } |
|
346 |
|
347 // |
|
348 // clear all but first line from scaled buffer |
|
349 // |
|
350 TInt clrbytes = tw * yStep * sizeof( TUint32 ); |
|
351 offset = tw; |
|
352 Mem::FillZ( cr+offset, clrbytes ); |
|
353 Mem::FillZ( cg+offset, clrbytes ); |
|
354 Mem::FillZ( cb+offset, clrbytes ); |
|
355 Mem::FillZ( cm+offset, clrbytes ); |
|
356 } |
|
357 } |
|
358 |
|
359 target->FinalizeSave(); |
|
360 |
|
361 delete bm.iData; |
|
362 |
|
363 CleanupStack::PopAndDestroy( 4 ); // cm,cb,cg,cr |
|
364 CleanupStack::PopAndDestroy( blk.iData ); |
|
365 CleanupStack::PopAndDestroy( target ); |
|
366 CleanupStack::PopAndDestroy( source ); |
|
367 CleanupStack::PopAndDestroy( 2 ); // file, fs |
|
368 |
|
369 if( iCallBack ) |
|
370 { |
|
371 iCallBack->JpegStatusCallBack( 100 ); |
|
372 } |
|
373 |
|
374 } |