Commit 76bcd00f74b0977b5f6ce12295aae987ea9ad4c1

  • avatar
  • aavit <qt-info @no…a.com>
  • Fri Feb 05 11:04:38 GMT 2010
Performance: Use QImage's smoothscaling instead of old private code

For historical reasons, QJpegHandler had its own smoothscaling code.
QImage's scaling has since evolved to become both faster and better, so
avoid code duplication and just use QImage for scaling.

Reviewed-by: Trond
src/plugins/imageformats/jpeg/qjpeghandler.cpp
(64 / 504)
  
5252#undef FAR
5353#endif
5454
55// hw: optimize smoothscaler for returning 24-bit images
56
5755// including jpeglib.h seems to be a little messy
5856extern "C" {
5957// mingw includes rpcndr.h but does not define boolean
7474
7575QT_BEGIN_NAMESPACE
7676
77//#define QT_NO_IMAGE_SMOOTHSCALE
78#ifndef QT_NO_IMAGE_SMOOTHSCALE
79class QImageSmoothScalerPrivate;
80class QImageSmoothScaler
81{
82public:
83 QImageSmoothScaler(const int w, const int h, const QImage &src);
84 QImageSmoothScaler(const int srcWidth, const int srcHeight,
85 const int dstWidth, const int dstHeight);
86
87 virtual ~QImageSmoothScaler(void);
88
89 QImage scale();
90
91private:
92 QImageSmoothScalerPrivate *d;
93 virtual QRgb *scanLine(const int line = 0, const QImage *src = 0);
94};
95
96class QImageSmoothScalerPrivate
97{
98public:
99 int cols;
100 int newcols;
101 int rows;
102 int newrows;
103 bool hasAlpha;
104
105 const QImage *src;
106
107 void setup(const int srcWidth, const int srcHeight, const int dstWidth,
108 const int dstHeight, bool hasAlphaChannel);
109};
110
111QImageSmoothScaler::QImageSmoothScaler(const int w, const int h,
112 const QImage &src)
113{
114 d = new QImageSmoothScalerPrivate;
115
116 d->setup(src.width(), src.height(), w, h, src.hasAlphaChannel() );
117 this->d->src = &src;
118}
119
120QImageSmoothScaler::QImageSmoothScaler(const int srcWidth, const int srcHeight,
121 const int dstWidth, const int dstHeight)
122{
123 d = new QImageSmoothScalerPrivate;
124 d->setup(srcWidth, srcHeight, dstWidth, dstHeight, 0);
125}
126
127void QImageSmoothScalerPrivate::setup(const int srcWidth, const int srcHeight,
128 const int dstWidth, const int dstHeight,
129 bool hasAlphaChannel)
130{
131 cols = srcWidth;
132 rows = srcHeight;
133 newcols = dstWidth;
134 newrows = dstHeight;
135 hasAlpha = hasAlphaChannel;
136}
137
138QImageSmoothScaler::~QImageSmoothScaler()
139{
140 delete d;
141}
142
143inline QRgb *QImageSmoothScaler::scanLine(const int line, const QImage *src)
144{
145 return (QRgb*)src->scanLine(line);
146}
147
148/*
149 This function uses code based on pnmscale.c by Jef Poskanzer.
150
151 pnmscale.c - read a portable anymap and scale it
152
153 Copyright (C) 1989, 1991 by Jef Poskanzer.
154
155 Permission to use, copy, modify, and distribute this software and its
156 documentation for any purpose and without fee is hereby granted, provided
157 that the above copyright notice appear in all copies and that both that
158 copyright notice and this permission notice appear in supporting
159 documentation. This software is provided "as is" without express or
160 implied warranty.
161*/
162
163QImage QImageSmoothScaler::scale()
164{
165 long SCALE;
166 long HALFSCALE;
167 QRgb *xelrow = 0;
168 QRgb *tempxelrow = 0;
169 QRgb *xP;
170 QRgb *nxP;
171 int row, rowsread;
172 int col, needtoreadrow;
173 uchar maxval = 255;
174 qreal xscale, yscale;
175 long sxscale, syscale;
176 long fracrowtofill, fracrowleft;
177 long *as;
178 long *rs;
179 long *gs;
180 long *bs;
181 int rowswritten = 0;
182 QImage dst;
183
184 if (d->cols > 4096) {
185 SCALE = 4096;
186 HALFSCALE = 2048;
187 } else {
188 int fac = 4096;
189 while (d->cols * fac > 4096)
190 fac /= 2;
191
192 SCALE = fac * d->cols;
193 HALFSCALE = fac * d->cols / 2;
194 }
195
196 xscale = (qreal)d->newcols / (qreal)d->cols;
197 yscale = (qreal)d->newrows / (qreal)d->rows;
198 sxscale = (long)(xscale * SCALE);
199 syscale = (long)(yscale * SCALE);
200
201 // shortcut Y scaling if possible
202 if (d->newrows != d->rows)
203 tempxelrow = new QRgb[d->cols];
204
205 if (d->hasAlpha) {
206 as = new long[d->cols];
207 for (col = 0; col < d->cols; ++col)
208 as[col] = HALFSCALE;
209 } else {
210 as = 0;
211 }
212 rs = new long[d->cols];
213 gs = new long[d->cols];
214 bs = new long[d->cols];
215 rowsread = 0;
216 fracrowleft = syscale;
217 needtoreadrow = 1;
218 for (col = 0; col < d->cols; ++col)
219 rs[col] = gs[col] = bs[col] = HALFSCALE;
220 fracrowtofill = SCALE;
221
222 dst = QImage(d->newcols, d->newrows, d->hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
223
224 for (row = 0; row < d->newrows; ++row) {
225 // First scale Y from xelrow into tempxelrow.
226 if (d->newrows == d->rows) {
227 // shortcut Y scaling if possible
228 tempxelrow = xelrow = scanLine(rowsread++, d->src);
229 } else {
230 while (fracrowleft < fracrowtofill) {
231 if (needtoreadrow && rowsread < d->rows)
232 xelrow = scanLine(rowsread++, d->src);
233 for (col = 0, xP = xelrow; col < d->cols; ++col, ++xP) {
234 if (as) {
235 as[col] += fracrowleft * qAlpha(*xP);
236 rs[col] += fracrowleft * qRed(*xP) * qAlpha(*xP) / 255;
237 gs[col] += fracrowleft * qGreen(*xP) * qAlpha(*xP) / 255;
238 bs[col] += fracrowleft * qBlue(*xP) * qAlpha(*xP) / 255;
239 } else {
240 rs[col] += fracrowleft * qRed(*xP);
241 gs[col] += fracrowleft * qGreen(*xP);
242 bs[col] += fracrowleft * qBlue(*xP);
243 }
244 }
245 fracrowtofill -= fracrowleft;
246 fracrowleft = syscale;
247 needtoreadrow = 1;
248 }
249 // Now fracrowleft is >= fracrowtofill, so we can produce a row.
250 if (needtoreadrow && rowsread < d->rows) {
251 xelrow = scanLine(rowsread++, d->src);
252 needtoreadrow = 0;
253 }
254 for (col = 0, xP = xelrow, nxP = tempxelrow; col < d->cols; ++col, ++xP, ++nxP) {
255 register long a, r, g, b;
256
257 if (as) {
258 r = rs[col] + fracrowtofill * qRed(*xP) * qAlpha(*xP) / 255;
259 g = gs[col] + fracrowtofill * qGreen(*xP) * qAlpha(*xP) / 255;
260 b = bs[col] + fracrowtofill * qBlue(*xP) * qAlpha(*xP) / 255;
261 a = as[col] + fracrowtofill * qAlpha(*xP);
262 if (a) {
263 r = r * 255 / a * SCALE;
264 g = g * 255 / a * SCALE;
265 b = b * 255 / a * SCALE;
266 }
267 } else {
268 r = rs[col] + fracrowtofill * qRed(*xP);
269 g = gs[col] + fracrowtofill * qGreen(*xP);
270 b = bs[col] + fracrowtofill * qBlue(*xP);
271 a = 0; // unwarn
272 }
273 r /= SCALE;
274 if (r > maxval)
275 r = maxval;
276 g /= SCALE;
277 if (g > maxval)
278 g = maxval;
279 b /= SCALE;
280 if (b > maxval)
281 b = maxval;
282 if (as) {
283 a /= SCALE;
284 if (a > maxval)
285 a = maxval;
286 *nxP = qRgba((int)r, (int)g, (int)b, (int)a);
287 as[col] = HALFSCALE;
288 } else {
289 *nxP = qRgb((int)r, (int)g, (int)b);
290 }
291 rs[col] = gs[col] = bs[col] = HALFSCALE;
292 }
293 fracrowleft -= fracrowtofill;
294 if (fracrowleft == 0) {
295 fracrowleft = syscale;
296 needtoreadrow = 1;
297 }
298 fracrowtofill = SCALE;
299 }
300
301 // Now scale X from tempxelrow into dst and write it out.
302 if (d->newcols == d->cols) {
303 // shortcut X scaling if possible
304 memcpy(dst.scanLine(rowswritten++), tempxelrow, d->newcols * 4);
305 } else {
306 register long a, r, g, b;
307 register long fraccoltofill, fraccolleft = 0;
308 register int needcol;
309
310 nxP = (QRgb *)dst.scanLine(rowswritten++);
311 QRgb *nxPEnd = nxP + d->newcols;
312 fraccoltofill = SCALE;
313 a = r = g = b = HALFSCALE;
314 needcol = 0;
315 for (col = 0, xP = tempxelrow; col < d->cols; ++col, ++xP) {
316 fraccolleft = sxscale;
317 while (fraccolleft >= fraccoltofill) {
318 if (needcol) {
319 ++nxP;
320 a = r = g = b = HALFSCALE;
321 }
322 if (as) {
323 r += fraccoltofill * qRed(*xP) * qAlpha(*xP) / 255;
324 g += fraccoltofill * qGreen(*xP) * qAlpha(*xP) / 255;
325 b += fraccoltofill * qBlue(*xP) * qAlpha(*xP) / 255;
326 a += fraccoltofill * qAlpha(*xP);
327 if (a) {
328 r = r * 255 / a * SCALE;
329 g = g * 255 / a * SCALE;
330 b = b * 255 / a * SCALE;
331 }
332 } else {
333 r += fraccoltofill * qRed(*xP);
334 g += fraccoltofill * qGreen(*xP);
335 b += fraccoltofill * qBlue(*xP);
336 }
337 r /= SCALE;
338 if (r > maxval)
339 r = maxval;
340 g /= SCALE;
341 if (g > maxval)
342 g = maxval;
343 b /= SCALE;
344 if (b > maxval)
345 b = maxval;
346 if (as) {
347 a /= SCALE;
348 if (a > maxval)
349 a = maxval;
350 *nxP = qRgba((int)r, (int)g, (int)b, (int)a);
351 } else {
352 *nxP = qRgb((int)r, (int)g, (int)b);
353 }
354 fraccolleft -= fraccoltofill;
355 fraccoltofill = SCALE;
356 needcol = 1;
357 }
358 if (fraccolleft > 0) {
359 if (needcol) {
360 ++nxP;
361 a = r = g = b = HALFSCALE;
362 needcol = 0;
363 }
364 if (as) {
365 a += fraccolleft * qAlpha(*xP);
366 r += fraccolleft * qRed(*xP) * qAlpha(*xP) / 255;
367 g += fraccolleft * qGreen(*xP) * qAlpha(*xP) / 255;
368 b += fraccolleft * qBlue(*xP) * qAlpha(*xP) / 255;
369 } else {
370 r += fraccolleft * qRed(*xP);
371 g += fraccolleft * qGreen(*xP);
372 b += fraccolleft * qBlue(*xP);
373 }
374 fraccoltofill -= fraccolleft;
375 }
376 }
377 if (fraccoltofill > 0) {
378 --xP;
379 if (as) {
380 a += fraccolleft * qAlpha(*xP);
381 r += fraccoltofill * qRed(*xP) * qAlpha(*xP) / 255;
382 g += fraccoltofill * qGreen(*xP) * qAlpha(*xP) / 255;
383 b += fraccoltofill * qBlue(*xP) * qAlpha(*xP) / 255;
384 if (a) {
385 r = r * 255 / a * SCALE;
386 g = g * 255 / a * SCALE;
387 b = b * 255 / a * SCALE;
388 }
389 } else {
390 r += fraccoltofill * qRed(*xP);
391 g += fraccoltofill * qGreen(*xP);
392 b += fraccoltofill * qBlue(*xP);
393 }
394 }
395 if (nxP < nxPEnd) {
396 r /= SCALE;
397 if (r > maxval)
398 r = maxval;
399 g /= SCALE;
400 if (g > maxval)
401 g = maxval;
402 b /= SCALE;
403 if (b > maxval)
404 b = maxval;
405 if (as) {
406 a /= SCALE;
407 if (a > maxval)
408 a = maxval;
409 *nxP = qRgba((int)r, (int)g, (int)b, (int)a);
410 } else {
411 *nxP = qRgb((int)r, (int)g, (int)b);
412 }
413 while (++nxP != nxPEnd)
414 nxP[0] = nxP[-1];
415 }
416 }
417 }
418
419 if (d->newrows != d->rows && tempxelrow)// Robust, tempxelrow might be 0 1 day
420 delete [] tempxelrow;
421 if (as) // Avoid purify complaint
422 delete [] as;
423 if (rs) // Robust, rs might be 0 one day
424 delete [] rs;
425 if (gs) // Robust, gs might be 0 one day
426 delete [] gs;
427 if (bs) // Robust, bs might be 0 one day
428 delete [] bs;
429
430 return dst;
431}
432
433class jpegSmoothScaler : public QImageSmoothScaler
434{
435public:
436 jpegSmoothScaler(struct jpeg_decompress_struct *info, const QSize& dstSize, const QRect& clipRect)
437 : QImageSmoothScaler(clipRect.width(), clipRect.height(),
438 dstSize.width(), dstSize.height())
439 {
440 cinfo = info;
441 clip = clipRect;
442 imageCache = QImage(info->output_width, 1, QImage::Format_RGB32);
443 }
444
445private:
446 QRect clip;
447 QImage imageCache;
448 struct jpeg_decompress_struct *cinfo;
449
450 QRgb *scanLine(const int line = 0, const QImage *src = 0)
451 {
452 QRgb *out;
453 uchar *in;
454
455 Q_UNUSED(line);
456 Q_UNUSED(src);
457
458 uchar* data = imageCache.bits();
459
460 // Read ahead if we haven't reached the first clipped scanline yet.
461 while (int(cinfo->output_scanline) < clip.y() &&
462 cinfo->output_scanline < cinfo->output_height)
463 jpeg_read_scanlines(cinfo, &data, 1);
464
465 // Read the next scanline. We assume that "line"
466 // will never be >= clip.height().
467 jpeg_read_scanlines(cinfo, &data, 1);
468 if (cinfo->output_scanline == cinfo->output_height)
469 jpeg_finish_decompress(cinfo);
470
471 out = ((QRgb*)data) + clip.x();
472
473 //
474 // The smooth scale algorithm only works on 32-bit images;
475 // convert from (8|24) bits to 32.
476 //
477 if (cinfo->output_components == 1) {
478 in = data + clip.right();
479 for (int i = clip.width(); i--; ) {
480 out[i] = qRgb(*in, *in, *in);
481 in--;
482 }
483 } else if (cinfo->out_color_space == JCS_CMYK) {
484 in = data + clip.right() * 4;
485 for (int i = clip.width(); i--; ) {
486 int k = in[3];
487 out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
488 in -= 4;
489 }
490 } else {
491 in = data + clip.right() * 3;
492 for (int i = clip.width(); i--; ) {
493 out[i] = qRgb(in[0], in[1], in[2]);
494 in -= 3;
495 }
496 }
497
498 return out;
499 }
500
501};
502#endif
503
50477struct my_error_mgr : public jpeg_error_mgr {
50578 jmp_buf setjmp_buffer;
50679};
415415 clip = clip.intersected(imageRect);
416416 }
417417
418#ifndef QT_NO_IMAGE_SMOOTHSCALE
419 if (scaledSize.isValid() && scaledSize != clip.size()
420 && quality >= HIGH_QUALITY_THRESHOLD) {
418 // Allocate memory for the clipped QImage.
419 if (!ensureValidImage(outImage, &cinfo, clip.size()))
420 longjmp(jerr.setjmp_buffer, 1);
421421
422 // Avoid memcpy() overhead if grayscale with no clipping.
423 bool quickGray = (cinfo.output_components == 1 &&
424 clip == imageRect);
425 if (!quickGray) {
426 // Ask the jpeg library to allocate a temporary row.
427 // The library will automatically delete it for us later.
428 // The libjpeg docs say we should do this before calling
429 // jpeg_start_decompress(). We can't use "new" here
430 // because we are inside the setjmp() block and an error
431 // in the jpeg input stream would cause a memory leak.
432 JSAMPARRAY rows = (cinfo.mem->alloc_sarray)
433 ((j_common_ptr)&cinfo, JPOOL_IMAGE,
434 cinfo.output_width * cinfo.output_components, 1);
435
422436 (void) jpeg_start_decompress(&cinfo);
423437
424 jpegSmoothScaler scaler(&cinfo, scaledSize, clip);
425 *outImage = scaler.scale();
426 } else
427#endif
428 {
429 // Allocate memory for the clipped QImage.
430 if (!ensureValidImage(outImage, &cinfo, clip.size()))
431 longjmp(jerr.setjmp_buffer, 1);
438 while (cinfo.output_scanline < cinfo.output_height) {
439 int y = int(cinfo.output_scanline) - clip.y();
440 if (y >= clip.height())
441 break; // We've read the entire clip region, so abort.
432442
433 // Avoid memcpy() overhead if grayscale with no clipping.
434 bool quickGray = (cinfo.output_components == 1 &&
435 clip == imageRect);
436 if (!quickGray) {
437 // Ask the jpeg library to allocate a temporary row.
438 // The library will automatically delete it for us later.
439 // The libjpeg docs say we should do this before calling
440 // jpeg_start_decompress(). We can't use "new" here
441 // because we are inside the setjmp() block and an error
442 // in the jpeg input stream would cause a memory leak.
443 JSAMPARRAY rows = (cinfo.mem->alloc_sarray)
444 ((j_common_ptr)&cinfo, JPOOL_IMAGE,
445 cinfo.output_width * cinfo.output_components, 1);
443 (void) jpeg_read_scanlines(&cinfo, rows, 1);
446444
447 (void) jpeg_start_decompress(&cinfo);
445 if (y < 0)
446 continue; // Haven't reached the starting line yet.
448447
449 while (cinfo.output_scanline < cinfo.output_height) {
450 int y = int(cinfo.output_scanline) - clip.y();
451 if (y >= clip.height())
452 break; // We've read the entire clip region, so abort.
453
454 (void) jpeg_read_scanlines(&cinfo, rows, 1);
455
456 if (y < 0)
457 continue; // Haven't reached the starting line yet.
458
459 if (cinfo.output_components == 3) {
460 // Expand 24->32 bpp.
461 uchar *in = rows[0] + clip.x() * 3;
462 QRgb *out = (QRgb*)outImage->scanLine(y);
463 for (int i = 0; i < clip.width(); ++i) {
464 *out++ = qRgb(in[0], in[1], in[2]);
465 in += 3;
466 }
467 } else if (cinfo.out_color_space == JCS_CMYK) {
468 // Convert CMYK->RGB.
469 uchar *in = rows[0] + clip.x() * 4;
470 QRgb *out = (QRgb*)outImage->scanLine(y);
471 for (int i = 0; i < clip.width(); ++i) {
472 int k = in[3];
473 *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
474 k * in[2] / 255);
475 in += 4;
476 }
477 } else if (cinfo.output_components == 1) {
478 // Grayscale.
479 memcpy(outImage->scanLine(y),
480 rows[0] + clip.x(), clip.width());
448 if (cinfo.output_components == 3) {
449 // Expand 24->32 bpp.
450 uchar *in = rows[0] + clip.x() * 3;
451 QRgb *out = (QRgb*)outImage->scanLine(y);
452 for (int i = 0; i < clip.width(); ++i) {
453 *out++ = qRgb(in[0], in[1], in[2]);
454 in += 3;
481455 }
456 } else if (cinfo.out_color_space == JCS_CMYK) {
457 // Convert CMYK->RGB.
458 uchar *in = rows[0] + clip.x() * 4;
459 QRgb *out = (QRgb*)outImage->scanLine(y);
460 for (int i = 0; i < clip.width(); ++i) {
461 int k = in[3];
462 *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
463 k * in[2] / 255);
464 in += 4;
465 }
466 } else if (cinfo.output_components == 1) {
467 // Grayscale.
468 memcpy(outImage->scanLine(y),
469 rows[0] + clip.x(), clip.width());
482470 }
483 } else {
484 // Load unclipped grayscale data directly into the QImage.
485 (void) jpeg_start_decompress(&cinfo);
486 while (cinfo.output_scanline < cinfo.output_height) {
487 uchar *row = outImage->scanLine(cinfo.output_scanline);
488 (void) jpeg_read_scanlines(&cinfo, &row, 1);
489 }
490471 }
472 } else {
473 // Load unclipped grayscale data directly into the QImage.
474 (void) jpeg_start_decompress(&cinfo);
475 while (cinfo.output_scanline < cinfo.output_height) {
476 uchar *row = outImage->scanLine(cinfo.output_scanline);
477 (void) jpeg_read_scanlines(&cinfo, &row, 1);
478 }
479 }
491480
492 if (cinfo.output_scanline == cinfo.output_height)
493 (void) jpeg_finish_decompress(&cinfo);
481 if (cinfo.output_scanline == cinfo.output_height)
482 (void) jpeg_finish_decompress(&cinfo);
494483
495 if (cinfo.density_unit == 1) {
496 outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
497 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
498 } else if (cinfo.density_unit == 2) {
499 outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
500 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
501 }
484 if (cinfo.density_unit == 1) {
485 outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
486 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
487 } else if (cinfo.density_unit == 2) {
488 outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
489 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
490 }
502491
503 if (scaledSize.isValid() && scaledSize != clip.size())
504 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
492 if (scaledSize.isValid() && scaledSize != clip.size()) {
493 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >= HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
505494 }
506495 }
507496

Comments

Add a new comment:

Login or create an account to post a comment

Add your comment