Commit 76bcd00f74b0977b5f6ce12295aae987ea9ad4c1
- Diff rendering mode:
- inline
- side by side
src/plugins/imageformats/jpeg/qjpeghandler.cpp
(64 / 504)
|   | |||
| 52 | 52 | #undef FAR | |
| 53 | 53 | #endif | |
| 54 | 54 | ||
| 55 | // hw: optimize smoothscaler for returning 24-bit images | ||
| 56 | |||
| 57 | 55 | // including jpeglib.h seems to be a little messy | |
| 58 | 56 | extern "C" { | |
| 59 | 57 | // mingw includes rpcndr.h but does not define boolean | |
| … | … | ||
| 74 | 74 | ||
| 75 | 75 | QT_BEGIN_NAMESPACE | |
| 76 | 76 | ||
| 77 | //#define QT_NO_IMAGE_SMOOTHSCALE | ||
| 78 | #ifndef QT_NO_IMAGE_SMOOTHSCALE | ||
| 79 | class QImageSmoothScalerPrivate; | ||
| 80 | class QImageSmoothScaler | ||
| 81 | { | ||
| 82 | public: | ||
| 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 | |||
| 91 | private: | ||
| 92 | QImageSmoothScalerPrivate *d; | ||
| 93 | virtual QRgb *scanLine(const int line = 0, const QImage *src = 0); | ||
| 94 | }; | ||
| 95 | |||
| 96 | class QImageSmoothScalerPrivate | ||
| 97 | { | ||
| 98 | public: | ||
| 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 | |||
| 111 | QImageSmoothScaler::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 | |||
| 120 | QImageSmoothScaler::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 | |||
| 127 | void 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 | |||
| 138 | QImageSmoothScaler::~QImageSmoothScaler() | ||
| 139 | { | ||
| 140 | delete d; | ||
| 141 | } | ||
| 142 | |||
| 143 | inline 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 | |||
| 163 | QImage 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 | |||
| 433 | class jpegSmoothScaler : public QImageSmoothScaler | ||
| 434 | { | ||
| 435 | public: | ||
| 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 | |||
| 445 | private: | ||
| 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 | |||
| 504 | 77 | struct my_error_mgr : public jpeg_error_mgr { | |
| 505 | 78 | jmp_buf setjmp_buffer; | |
| 506 | 79 | }; | |
| … | … | ||
| 415 | 415 | clip = clip.intersected(imageRect); | |
| 416 | 416 | } | |
| 417 | 417 | ||
| 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); | ||
| 421 | 421 | ||
| 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 | |||
| 422 | 436 | (void) jpeg_start_decompress(&cinfo); | |
| 423 | 437 | ||
| 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. | ||
| 432 | 442 | ||
| 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); | ||
| 446 | 444 | ||
| 447 | (void) jpeg_start_decompress(&cinfo); | ||
| 445 | if (y < 0) | ||
| 446 | continue; // Haven't reached the starting line yet. | ||
| 448 | 447 | ||
| 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; | ||
| 481 | 455 | } | |
| 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()); | ||
| 482 | 470 | } | |
| 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 | } | ||
| 490 | 471 | } | |
| 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 | } | ||
| 491 | 480 | ||
| 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); | ||
| 494 | 483 | ||
| 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 | } | ||
| 502 | 491 | ||
| 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); | ||
| 505 | 494 | } | |
| 506 | 495 | } | |
| 507 | 496 |
Comments
Add a new comment:
Login or create an account to post a comment
Add your comment
Please log in to comment

