1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7
8 #include <cassert>
9 #include <cmath>
10 #include <cstdlib>
11 #include <memory>
12 #include <csetjmp>
13
14 #include <QByteArray>
15 #include <QFile>
16 #include <QImageReader>
17 #include <QMessageBox>
18 #include <QList>
19 #include <QScopedPointer>
20
21 #include "cmsettings.h"
22 #include "commonstrings.h"
23 #include "exif.h"
24 #include "rawimage.h"
25 #include "scclocale.h"
26 #include "sccolorengine.h"
27 #include "scimagecacheproxy.h"
28 #include "scstreamfilter.h"
29 #include "scimage.h"
30 #include "scpaths.h"
31 #include "scribuscore.h"
32 #include "scstreamfilter_jpeg.h"
33 #include "sctextstream.h"
34 #include "util.h"
35 #include "util_color.h"
36 #include "util_formats.h"
37 #include "util_ghostscript.h"
38
39 #include "imagedataloaders/scimgdataloader_gimp.h"
40 #ifdef GMAGICK_FOUND
41 #include "imagedataloaders/scimgdataloader_gmagick.h"
42 #endif
43 #include "imagedataloaders/scimgdataloader_jpeg.h"
44 #include "imagedataloaders/scimgdataloader_ora.h"
45 #include "imagedataloaders/scimgdataloader_kra.h"
46 #include "imagedataloaders/scimgdataloader_pict.h"
47 #include "imagedataloaders/scimgdataloader_pdf.h"
48 #include "imagedataloaders/scimgdataloader_pgf.h"
49 #include "imagedataloaders/scimgdataloader_png.h"
50 #include "imagedataloaders/scimgdataloader_ps.h"
51 #include "imagedataloaders/scimgdataloader_psd.h"
52 #include "imagedataloaders/scimgdataloader_qt.h"
53 #include "imagedataloaders/scimgdataloader_tiff.h"
54 #include "imagedataloaders/scimgdataloader_wpg.h"
55
56
57 using namespace std;
58
ScImage(const QImage & image)59 ScImage::ScImage(const QImage & image) : QImage(image)
60 {
61 initialize();
62 }
63
64
65 // ScImage will use implicit sharing:
ScImage(const ScImage & image)66 ScImage::ScImage(const ScImage & image) : QImage(image.copy())
67 {
68 initialize();
69 }
70
71
ScImage()72 ScImage::ScImage()
73 {
74 initialize();
75 }
76
ScImage(int width,int height)77 ScImage::ScImage( int width, int height ) : QImage( width, height, QImage::Format_ARGB32 )
78 {
79 initialize();
80 }
81
qImage()82 const QImage& ScImage::qImage()
83 {
84 return *this;
85 }
86
qImagePtr()87 QImage* ScImage::qImagePtr()
88 {
89 return this;
90 }
91
scaled(int w,int h,Qt::AspectRatioMode mode,Qt::TransformationMode transformMode) const92 QImage ScImage::scaled(int w, int h, Qt::AspectRatioMode mode, Qt::TransformationMode transformMode) const
93 {
94 return QImage::scaled(w, h, mode, transformMode);
95 }
96
97
initialize()98 void ScImage::initialize()
99 {
100 imgInfo.xres = 72;
101 imgInfo.yres = 72;
102 imgInfo.colorspace = ColorSpaceRGB;
103 imgInfo.valid = false;
104 imgInfo.isRequest = false;
105 imgInfo.isEmbedded = false;
106 imgInfo.progressive = false;
107 imgInfo.exifDataValid = false;
108 imgInfo.lowResType = 1;
109 imgInfo.lowResScale = 1.0;
110 imgInfo.PDSpathData.clear();
111 imgInfo.RequestProps.clear();
112 imgInfo.clipPath.clear();
113 imgInfo.usedPath.clear();
114 imgInfo.profileName.clear();
115 imgInfo.embeddedProfileName.clear();
116 imgInfo.layerInfo.clear();
117 imgInfo.duotoneColors.clear();
118 imgInfo.exifInfo.cameraName.clear();
119 imgInfo.exifInfo.cameraVendor.clear();
120 imgInfo.exifInfo.thumbnail = QImage();
121 imgInfo.BBoxX = 0;
122 imgInfo.BBoxH = 0;
123 }
124
~ScImage()125 ScImage::~ScImage()
126 {
127 }
128
applyEffect(const ScImageEffectList & effectsList,ColorList & colors,bool cmyk)129 void ScImage::applyEffect(const ScImageEffectList& effectsList, ColorList& colors, bool cmyk)
130 {
131 if (effectsList.count() <= 0)
132 return;
133 ScribusDoc* doc = colors.document();
134
135 for (int i = 0; i < effectsList.count(); ++i)
136 {
137 const ImageEffect& effect = effectsList.at(i);
138 if (effect.effectCode == ImageEffect::EF_INVERT)
139 invert(cmyk);
140 if (effect.effectCode == ImageEffect::EF_GRAYSCALE)
141 toGrayscale(cmyk);
142 if (effect.effectCode == ImageEffect::EF_COLORIZE)
143 {
144 QString tmpstr = effect.effectParameters;
145 QString col = CommonStrings::None;
146 int shading = 100;
147 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
148 // fp >> col;
149 col = fp.readLine();
150 fp >> shading;
151 colorize(doc, colors[col], shading, cmyk);
152 }
153 if (effect.effectCode == ImageEffect::EF_BRIGHTNESS)
154 {
155 QString tmpstr = effect.effectParameters;
156 int brightnessValue = 0;
157 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
158 fp >> brightnessValue;
159 brightness(brightnessValue, cmyk);
160 }
161 if (effect.effectCode == ImageEffect::EF_CONTRAST)
162 {
163 QString tmpstr = effect.effectParameters;
164 int contrastValue = 0;
165 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
166 fp >> contrastValue;
167 contrast(contrastValue, cmyk);
168 }
169 if (effect.effectCode == ImageEffect::EF_SHARPEN)
170 {
171 QString tmpstr = effect.effectParameters;
172 double radius, sigma;
173 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
174 fp >> radius;
175 fp >> sigma;
176 sharpen(radius, sigma);
177 }
178 if (effect.effectCode == ImageEffect::EF_BLUR)
179 {
180 QString tmpstr = effect.effectParameters;
181 double radius, sigma;
182 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
183 fp >> radius;
184 fp >> sigma;
185 blur(static_cast<int>(radius));
186 }
187 if (effect.effectCode == ImageEffect::EF_SOLARIZE)
188 {
189 QString tmpstr = effect.effectParameters;
190 double sigma;
191 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
192 fp >> sigma;
193 solarize(sigma, cmyk);
194 }
195 if (effect.effectCode == ImageEffect::EF_DUOTONE)
196 {
197 QString tmpstr = effect.effectParameters;
198 QString col1 = CommonStrings::None;
199 int shading1 = 100;
200 QString col2 = CommonStrings::None;
201 int shading2 = 100;
202 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
203 col1 = fp.readLine();
204 col2 = fp.readLine();
205 fp >> shading1;
206 fp >> shading2;
207 int numVals;
208 double xval, yval;
209 FPointArray curve1;
210 curve1.resize(0);
211 fp >> numVals;
212 for (int nv = 0; nv < numVals; nv++)
213 {
214 fp >> xval;
215 fp >> yval;
216 curve1.addPoint(xval, yval);
217 }
218 int lin1;
219 fp >> lin1;
220 FPointArray curve2;
221 curve2.resize(0);
222 fp >> numVals;
223 for (int nv = 0; nv < numVals; nv++)
224 {
225 fp >> xval;
226 fp >> yval;
227 curve2.addPoint(xval, yval);
228 }
229 int lin2;
230 fp >> lin2;
231 duotone(doc, colors[col1], shading1, curve1, lin1, colors[col2], shading2, curve2, lin2, cmyk);
232 }
233 if (effect.effectCode == ImageEffect::EF_TRITONE)
234 {
235 QString tmpstr = effect.effectParameters;
236 QString col1 = CommonStrings::None;
237 QString col2 = CommonStrings::None;
238 QString col3 = CommonStrings::None;
239 int shading1 = 100;
240 int shading2 = 100;
241 int shading3 = 100;
242 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
243 col1 = fp.readLine();
244 col2 = fp.readLine();
245 col3 = fp.readLine();
246 fp >> shading1;
247 fp >> shading2;
248 fp >> shading3;
249 int numVals;
250 double xval, yval;
251 FPointArray curve1;
252 curve1.resize(0);
253 fp >> numVals;
254 for (int nv = 0; nv < numVals; nv++)
255 {
256 fp >> xval;
257 fp >> yval;
258 curve1.addPoint(xval, yval);
259 }
260 int lin1;
261 fp >> lin1;
262 FPointArray curve2;
263 curve2.resize(0);
264 fp >> numVals;
265 for (int nv = 0; nv < numVals; nv++)
266 {
267 fp >> xval;
268 fp >> yval;
269 curve2.addPoint(xval, yval);
270 }
271 int lin2;
272 fp >> lin2;
273 FPointArray curve3;
274 curve3.resize(0);
275 fp >> numVals;
276 for (int nv = 0; nv < numVals; nv++)
277 {
278 fp >> xval;
279 fp >> yval;
280 curve3.addPoint(xval, yval);
281 }
282 int lin3;
283 fp >> lin3;
284 tritone(doc, colors[col1], shading1, curve1, lin1, colors[col2], shading2, curve2, lin2, colors[col3], shading3, curve3, lin3, cmyk);
285 }
286 if (effect.effectCode == ImageEffect::EF_QUADTONE)
287 {
288 QString tmpstr = effect.effectParameters;
289 QString col1 = CommonStrings::None;
290 QString col2 = CommonStrings::None;
291 QString col3 = CommonStrings::None;
292 QString col4 = CommonStrings::None;
293 int shading1 = 100;
294 int shading2 = 100;
295 int shading3 = 100;
296 int shading4 = 100;
297 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
298 col1 = fp.readLine();
299 col2 = fp.readLine();
300 col3 = fp.readLine();
301 col4 = fp.readLine();
302 fp >> shading1;
303 fp >> shading2;
304 fp >> shading3;
305 fp >> shading4;
306 int numVals;
307 double xval, yval;
308 FPointArray curve1;
309 curve1.resize(0);
310 fp >> numVals;
311 for (int nv = 0; nv < numVals; nv++)
312 {
313 fp >> xval;
314 fp >> yval;
315 curve1.addPoint(xval, yval);
316 }
317 int lin1;
318 fp >> lin1;
319 FPointArray curve2;
320 curve2.resize(0);
321 fp >> numVals;
322 for (int nv = 0; nv < numVals; nv++)
323 {
324 fp >> xval;
325 fp >> yval;
326 curve2.addPoint(xval, yval);
327 }
328 int lin2;
329 fp >> lin2;
330 FPointArray curve3;
331 curve3.resize(0);
332 fp >> numVals;
333 for (int nv = 0; nv < numVals; nv++)
334 {
335 fp >> xval;
336 fp >> yval;
337 curve3.addPoint(xval, yval);
338 }
339 int lin3;
340 fp >> lin3;
341 FPointArray curve4;
342 curve4.resize(0);
343 fp >> numVals;
344 for (int nv = 0; nv < numVals; nv++)
345 {
346 fp >> xval;
347 fp >> yval;
348 curve4.addPoint(xval, yval);
349 }
350 int lin4;
351 fp >> lin4;
352 quadtone(doc, colors[col1], shading1, curve1, lin1, colors[col2], shading2, curve2, lin2, colors[col3], shading3, curve3, lin3, colors[col4], shading4, curve4, lin4, cmyk);
353 }
354 if (effect.effectCode == ImageEffect::EF_GRADUATE)
355 {
356 QString tmpstr = effect.effectParameters;
357 int numVals;
358 double xval, yval;
359 FPointArray curve;
360 curve.resize(0);
361 ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
362 fp >> numVals;
363 for (int nv = 0; nv < numVals; nv++)
364 {
365 fp >> xval;
366 fp >> yval;
367 curve.addPoint(xval, yval);
368 }
369 int lin;
370 fp >> lin;
371 doGraduate(curve, cmyk, lin);
372 }
373 }
374 }
375
solarize(double factor,bool cmyk)376 void ScImage::solarize(double factor, bool cmyk)
377 {
378 QVector<int> curveTable(256);
379 int fk = qRound(255 / factor);
380 for (int i = 0; i < 256; ++i)
381 {
382 curveTable[i] = qMin(255, static_cast<int>(i / fk) * fk);
383 }
384 applyCurve(curveTable, cmyk);
385 }
386
387 // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
blur(int radius)388 void ScImage::blur(int radius)
389 {
390 if (radius < 1) {
391 return;
392 }
393
394 QRgb *pix = (QRgb*) bits();
395 int w = width();
396 int h = height();
397 int wm = w - 1;
398 int hm = h - 1;
399 int wh = w * h;
400 int div = radius + radius + 1;
401
402 int *r = new int[wh];
403 int *g = new int[wh];
404 int *b = new int[wh];
405 int *a = new int[wh];
406 int rsum, gsum, bsum, asum, x, y, i, yp, yi, yw;
407 QRgb p;
408 int *vmin = new int[qMax(w, h)];
409
410 int divsum = (div + 1) >> 1;
411 divsum *= divsum;
412 int *dv = new int[256*divsum];
413 for (i = 0; i < 256 * divsum; ++i) {
414 dv[i] = (i / divsum);
415 }
416
417 yw = yi = 0;
418
419 int **stack = new int*[div];
420 for (int i = 0; i < div; ++i) {
421 stack[i] = new int[4];
422 }
423
424 int stackpointer;
425 int stackstart;
426 int *sir;
427 int rbs;
428 int r1 = radius + 1;
429 int routsum, goutsum, boutsum, aoutsum;
430 int rinsum, ginsum, binsum, ainsum;
431
432 for (y = 0; y < h; ++y)
433 {
434 rinsum = ginsum = binsum = ainsum
435 = routsum = goutsum = boutsum = aoutsum
436 = rsum = gsum = bsum = asum = 0;
437 for (i = -radius; i <= radius; ++i)
438 {
439 p = pix[yi + qMin(wm, qMax(i, 0))];
440 sir = stack[i + radius];
441 sir[0] = qRed(p);
442 sir[1] = qGreen(p);
443 sir[2] = qBlue(p);
444 sir[3] = qAlpha(p);
445
446 rbs = r1 - abs(i);
447 rsum += sir[0] * rbs;
448 gsum += sir[1] * rbs;
449 bsum += sir[2] * rbs;
450 asum += sir[3] * rbs;
451
452 if (i > 0)
453 {
454 rinsum += sir[0];
455 ginsum += sir[1];
456 binsum += sir[2];
457 ainsum += sir[3];
458 }
459 else
460 {
461 routsum += sir[0];
462 goutsum += sir[1];
463 boutsum += sir[2];
464 aoutsum += sir[3];
465 }
466 }
467 stackpointer = radius;
468
469 for (x=0; x < w; ++x)
470 {
471 r[yi] = dv[rsum];
472 g[yi] = dv[gsum];
473 b[yi] = dv[bsum];
474 a[yi] = dv[asum];
475
476 rsum -= routsum;
477 gsum -= goutsum;
478 bsum -= boutsum;
479 asum -= aoutsum;
480
481 stackstart = stackpointer - radius + div;
482 sir = stack[stackstart % div];
483
484 routsum -= sir[0];
485 goutsum -= sir[1];
486 boutsum -= sir[2];
487 aoutsum -= sir[3];
488
489 if (y == 0)
490 {
491 vmin[x] = qMin(x + radius + 1, wm);
492 }
493 p = pix[yw + vmin[x]];
494
495 sir[0] = qRed(p);
496 sir[1] = qGreen(p);
497 sir[2] = qBlue(p);
498 sir[3] = qAlpha(p);
499
500 rinsum += sir[0];
501 ginsum += sir[1];
502 binsum += sir[2];
503 ainsum += sir[3];
504
505 rsum += rinsum;
506 gsum += ginsum;
507 bsum += binsum;
508 asum += ainsum;
509
510 stackpointer = (stackpointer+1)%div;
511 sir = stack[(stackpointer)%div];
512
513 routsum += sir[0];
514 goutsum += sir[1];
515 boutsum += sir[2];
516 aoutsum += sir[3];
517
518 rinsum -= sir[0];
519 ginsum -= sir[1];
520 binsum -= sir[2];
521 ainsum -= sir[3];
522
523 ++yi;
524 }
525 yw += w;
526 }
527 for (x=0; x < w; ++x)
528 {
529 rinsum = ginsum = binsum = ainsum
530 = routsum = goutsum = boutsum = aoutsum
531 = rsum = gsum = bsum = asum = 0;
532
533 yp =- radius * w;
534
535 for (i=-radius; i <= radius; ++i)
536 {
537 yi = qMax(0, yp) + x;
538
539 sir = stack[i + radius];
540
541 sir[0] = r[yi];
542 sir[1] = g[yi];
543 sir[2] = b[yi];
544 sir[3] = a[yi];
545
546 rbs = r1 - abs(i);
547
548 rsum += r[yi]*rbs;
549 gsum += g[yi]*rbs;
550 bsum += b[yi]*rbs;
551 asum += a[yi]*rbs;
552
553 if (i > 0)
554 {
555 rinsum += sir[0];
556 ginsum += sir[1];
557 binsum += sir[2];
558 ainsum += sir[3];
559 }
560 else
561 {
562 routsum += sir[0];
563 goutsum += sir[1];
564 boutsum += sir[2];
565 aoutsum += sir[3];
566 }
567
568 if (i < hm)
569 {
570 yp += w;
571 }
572 }
573
574 yi = x;
575 stackpointer = radius;
576
577 for (y=0; y < h; ++y)
578 {
579 pix[yi] = qRgba(dv[rsum], dv[gsum], dv[bsum], dv[asum]);
580
581 rsum -= routsum;
582 gsum -= goutsum;
583 bsum -= boutsum;
584 asum -= aoutsum;
585
586 stackstart = stackpointer-radius+div;
587 sir = stack[stackstart%div];
588
589 routsum -= sir[0];
590 goutsum -= sir[1];
591 boutsum -= sir[2];
592 aoutsum -= sir[3];
593
594 if (x==0)
595 {
596 vmin[y] = qMin(y + r1,hm)*w;
597 }
598 p = x + vmin[y];
599
600 sir[0] = r[p];
601 sir[1] = g[p];
602 sir[2] = b[p];
603 sir[3] = a[p];
604
605 rinsum += sir[0];
606 ginsum += sir[1];
607 binsum += sir[2];
608 ainsum += sir[3];
609
610 rsum += rinsum;
611 gsum += ginsum;
612 bsum += binsum;
613 asum += ainsum;
614
615 stackpointer = (stackpointer + 1) % div;
616 sir = stack[stackpointer];
617
618 routsum += sir[0];
619 goutsum += sir[1];
620 boutsum += sir[2];
621 aoutsum += sir[3];
622
623 rinsum -= sir[0];
624 ginsum -= sir[1];
625 binsum -= sir[2];
626 ainsum -= sir[3];
627
628 yi += w;
629 }
630 }
631 delete [] r;
632 delete [] g;
633 delete [] b;
634 delete [] a;
635 delete [] vmin;
636 delete [] dv;
637
638 for (int i = 0; i < div; ++i)
639 {
640 delete [] stack[i];
641 }
642 delete [] stack;
643 }
644
convolveImage(QImage * dest,const unsigned int order,const double * kernel)645 bool ScImage::convolveImage(QImage *dest, const unsigned int order, const double *kernel)
646 {
647 double red, green, blue, alpha;
648 const double *k;
649 unsigned int *q;
650 int x, y, mx, my, sx, sy;
651 long i;
652 int mcx, mcy;
653 long widthk = order;
654 if ((widthk % 2) == 0)
655 return false;
656 double *normal_kernel = (double *)malloc(widthk*widthk*sizeof(double));
657 if (!normal_kernel)
658 return false;
659 *dest = QImage(width(), height(), QImage::Format_ARGB32);
660 double normalize = 0.0;
661 for (i=0; i < (widthk * widthk); i++)
662 normalize += kernel[i];
663 if (fabs(normalize) <= 1.0e-12)
664 normalize = 1.0;
665 normalize = 1.0 / normalize;
666 for (i = 0; i < (widthk * widthk); i++)
667 normal_kernel[i] = normalize*kernel[i];
668 for (y = 0; y < dest->height(); ++y)
669 {
670 sy = y - (widthk / 2);
671 q = (unsigned int *) dest->scanLine(y);
672 for (x = 0; x < dest->width(); ++x)
673 {
674 k = normal_kernel;
675 red = green = blue = alpha = 0;
676 sy = y - (widthk / 2);
677 for (mcy = 0; mcy < widthk; ++mcy, ++sy)
678 {
679 my = sy < 0 ? 0 : sy > height() - 1 ? height() - 1 : sy;
680 sx = x + (-widthk / 2);
681 for (mcx=0; mcx < widthk; ++mcx, ++sx)
682 {
683 mx = sx < 0 ? 0 : sx > width()-1 ? width()-1 : sx;
684 int px = pixel(mx, my);
685 red += (*k)*(qRed(px) * 257);
686 green += (*k)*(qGreen(px) * 257);
687 blue += (*k)*(qBlue(px) * 257);
688 alpha += (*k)*(qAlpha(px) * 257);
689 ++k;
690 }
691 }
692 red = red < 0 ? 0 : red > 65535 ? 65535 : red + 0.5;
693 green = green < 0 ? 0 : green > 65535 ? 65535 : green + 0.5;
694 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue + 0.5;
695 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha + 0.5;
696 *q++ = qRgba((unsigned char)(red / 257UL),
697 (unsigned char)(green / 257UL),
698 (unsigned char)(blue / 257UL),
699 (unsigned char)(alpha / 257UL));
700 }
701 }
702 free(normal_kernel);
703 return(true);
704 }
705
getOptimalKernelWidth(double radius,double sigma)706 int ScImage::getOptimalKernelWidth(double radius, double sigma)
707 {
708 double normalize, value;
709 long width;
710 long u;
711 assert(sigma != 0.0);
712 if (radius > 0.0)
713 return((int)(2.0 * ceil(radius) + 1.0));
714 for (width = 5; ;)
715 {
716 normalize = 0.0;
717 for (u= (-width / 2); u <= (width / 2); u++)
718 normalize += exp(-((double) u * u) / (2.0 * sigma * sigma)) / (2.50662827463100024161235523934010416269302368164062 * sigma);
719 u = width / 2;
720 value = exp(-((double) u*u) / (2.0 * sigma * sigma)) / (2.50662827463100024161235523934010416269302368164062 * sigma) / normalize;
721 if ((long)(65535 * value) <= 0)
722 break;
723 width += 2;
724 }
725 return ((int) width - 2);
726 }
727
sharpen(double radius,double sigma)728 void ScImage::sharpen(double radius, double sigma)
729 {
730 double alpha, normalize, *kernel;
731 int widthk;
732 long i, u, v;
733 QImage dest;
734 if (sigma == 0.0)
735 return;
736 widthk = getOptimalKernelWidth(radius, sigma);
737 if ((widthk <= 0) || (width() < widthk))
738 return;
739 kernel = (double *) malloc(widthk * widthk * sizeof(double));
740 if (!kernel)
741 return;
742 i = 0;
743 normalize = 0.0;
744 for (v= (-widthk / 2); v <= (widthk / 2); v++)
745 {
746 for (u= (-widthk / 2); u <= (widthk / 2); u++)
747 {
748 alpha = exp(-((double) u * u + v * v) / (2.0 * sigma * sigma));
749 kernel[i] = alpha / (2.0 * 3.14159265358979323846264338327950288419716939937510 * sigma * sigma);
750 normalize += kernel[i];
751 i++;
752 }
753 }
754 kernel[i / 2] = (-2.0) * normalize;
755 convolveImage(&dest, widthk, kernel);
756 free(kernel);
757
758 for (int yi = 0; yi < dest.height(); ++yi)
759 {
760 QRgb *s = (QRgb*) dest.scanLine(yi);
761 QRgb *d = (QRgb*) scanLine(yi);
762 for (int xi = 0; xi < dest.width(); ++xi)
763 {
764 (*d) = (*s);
765 s++;
766 d++;
767 }
768 }
769 }
770
contrast(int contrastValue,bool cmyk)771 void ScImage::contrast(int contrastValue, bool cmyk)
772 {
773 QVector<int> curveTable(256);
774 QPoint p1(0,0 - contrastValue);
775 QPoint p2(256, 256 + contrastValue);
776 double mc = (p1.y() - p2.y()) / (double)(p1.x() - p2.x());
777 for (int i = 0; i < 256; ++i)
778 {
779 curveTable[i] = qMin(255, qMax(0, int(i * mc) + p1.y()));
780 }
781 applyCurve(curveTable, cmyk);
782 }
783
brightness(int brightnessValue,bool cmyk)784 void ScImage::brightness(int brightnessValue, bool cmyk)
785 {
786 QVector<int> curveTable(256);
787 QPoint p1(0,0 + brightnessValue);
788 QPoint p2(256, 256 + brightnessValue);
789 double mc = (p1.y() - p2.y()) / (double)(p1.x() - p2.x());
790 for (int i = 0; i < 256; ++i)
791 {
792 curveTable[i] = qMin(255, qMax(0, int(i * mc) + p1.y()));
793 }
794 applyCurve(curveTable, cmyk);
795 }
796
doGraduate(FPointArray curve,bool cmyk,bool linear)797 void ScImage::doGraduate(FPointArray curve, bool cmyk, bool linear)
798 {
799 QVector<int> curveTable(256);
800 for (int x = 0 ; x < 256 ; x++)
801 {
802 curveTable[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve, x / 255.0, linear) * 255)));
803 }
804 applyCurve(curveTable, cmyk);
805 }
806
applyCurve(const QVector<int> & curveTable,bool cmyk)807 void ScImage::applyCurve(const QVector<int>& curveTable, bool cmyk)
808 {
809 int h = height();
810 int w = width();
811 QRgb *s;
812 QRgb r;
813 int c, m, y, k;
814 unsigned char *p;
815 for (int yi=0; yi < h; ++yi)
816 {
817 s = (QRgb*)(scanLine( yi ));
818 for (int xi=0; xi < w; ++xi)
819 {
820 r = *s;
821 if (cmyk)
822 {
823 p = (unsigned char *) s;
824 p[0] = 255 - curveTable[255 - p[0]];
825 p[1] = 255 - curveTable[255 - p[1]];
826 p[2] = 255 - curveTable[255 - p[2]];
827 p[3] = 255 - curveTable[255 - p[3]];
828 }
829 else
830 {
831 c = curveTable[qRed(r)];
832 m = curveTable[qGreen(r)];
833 y = curveTable[qBlue(r)];
834 k = qAlpha(r);
835 *s = qRgba(c, m, y, k);
836 }
837 s++;
838 }
839 }
840 }
841
colorize(ScribusDoc * doc,ScColor color,int shade,bool cmyk)842 void ScImage::colorize(ScribusDoc* doc, ScColor color, int shade, bool cmyk)
843 {
844 int h = height();
845 int w = width();
846 int cc, cm, cy, ck;
847 int hu, sa, v;
848 ScColor tmp2;
849 QColor tmpR;
850 QRgb *s;
851 QRgb r;
852 double k;
853 int cc2, cm2, cy2, k2;
854 if (cmyk)
855 {
856 CMYKColor cmykCol;
857 ScColorEngine::getShadeColorCMYK(color, doc, cmykCol, shade);
858 cmykCol.getValues(cc, cm, cy, ck);
859 }
860 else
861 {
862 ck = 0;
863 RGBColor rgbCol;
864 ScColorEngine::getShadeColorRGB(color, doc, rgbCol, shade);
865 rgbCol.getValues(cc, cm, cy);
866 }
867 for (int yi = 0; yi < h; ++yi)
868 {
869 s = (QRgb*)(scanLine( yi ));
870 for (int xi = 0; xi < w; ++xi)
871 {
872 r = *s;
873 if (cmyk)
874 {
875 k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255) / 255.0;
876 *s = qRgba(qMin(qRound(cc*k), 255), qMin(qRound(cm*k), 255), qMin(qRound(cy*k), 255), qMin(qRound(ck*k), 255));
877 }
878 else
879 {
880 k2 = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
881 tmpR.setRgb(cc, cm, cy);
882 tmpR.getHsv(&hu, &sa, &v);
883 tmpR.setHsv(hu, sa * k2 / 255, 255 - ((255 - v) * k2 / 255));
884 tmpR.getRgb(&cc2, &cm2, &cy2);
885 *s = qRgba(cc2, cm2, cy2, qAlpha(r));
886 }
887 s++;
888 }
889 }
890 }
891
duotone(ScribusDoc * doc,ScColor color1,int shade1,FPointArray curve1,bool lin1,ScColor color2,int shade2,FPointArray curve2,bool lin2,bool cmyk)892 void ScImage::duotone(ScribusDoc* doc, ScColor color1, int shade1, FPointArray curve1, bool lin1, ScColor color2, int shade2, FPointArray curve2, bool lin2, bool cmyk)
893 {
894 int h = height();
895 int w = width();
896 int c, c1, m, m1, y, y1, k, k1;
897 int cn, c1n, mn, m1n, yn, y1n, kn, k1n;
898 uchar cb;
899 QVector<int> curveTable1;
900 QVector<int> curveTable2;
901 CMYKColor cmykCol;
902 ScColorEngine::getShadeColorCMYK(color1, doc, cmykCol, shade1);
903 cmykCol.getValues(c, m, y, k);
904 ScColorEngine::getShadeColorCMYK(color2, doc, cmykCol, shade2);
905 cmykCol.getValues(c1, m1, y1, k1);
906 curveTable1.resize(256);
907 for (int x = 0 ; x < 256 ; x++)
908 {
909 curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve1, x / 255.0, lin1) * 255)));
910 }
911 curveTable2.resize(256);
912 for (int x = 0 ; x < 256 ; x++)
913 {
914 curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin2) * 255)));
915 }
916 for (int yi=0; yi < h; ++yi)
917 {
918 QRgb * s = (QRgb*)(scanLine( yi ));
919 for (int xi=0; xi < w; ++xi)
920 {
921 QRgb r = *s;
922 if (cmyk)
923 cb = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
924 else
925 cb = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
926 cn = qMin((c * curveTable1[(int)cb]) >> 8, 255);
927 mn = qMin((m * curveTable1[(int)cb]) >> 8, 255);
928 yn = qMin((y * curveTable1[(int)cb]) >> 8, 255);
929 kn = qMin((k * curveTable1[(int)cb]) >> 8, 255);
930 c1n = qMin((c1 * curveTable1[(int)cb]) >> 8, 255);
931 m1n = qMin((m1 * curveTable2[(int)cb]) >> 8, 255);
932 y1n = qMin((y1 * curveTable2[(int)cb]) >> 8, 255);
933 k1n = qMin((k1 * curveTable2[(int)cb]) >> 8, 255);
934 ScColor col = ScColor(qMin(cn + c1n, 255), qMin(mn + m1n, 255), qMin(yn + y1n, 255), qMin(kn + k1n, 255));
935 if (cmyk)
936 col.getCMYK(&cn, &mn, &yn, &kn);
937 else
938 {
939 col.getRawRGBColor(&cn, &mn, &yn);
940 kn = qAlpha(r);
941 }
942 *s = qRgba(cn, mn, yn, kn);
943 s++;
944 }
945 }
946 }
947
tritone(ScribusDoc * doc,ScColor color1,int shade1,FPointArray curve1,bool lin1,ScColor color2,int shade2,FPointArray curve2,bool lin2,ScColor color3,int shade3,const FPointArray & curve3,bool lin3,bool cmyk)948 void ScImage::tritone(ScribusDoc* doc, ScColor color1, int shade1, FPointArray curve1, bool lin1, ScColor color2, int shade2, FPointArray curve2, bool lin2, ScColor color3, int shade3, const FPointArray& curve3, bool lin3, bool cmyk)
949 {
950 int h = height();
951 int w = width();
952 int c, c1, c2, m, m1, m2, y, y1, y2, k, k1, k2;
953 int cn, c1n, c2n, mn, m1n, m2n, yn, y1n, y2n, kn, k1n, k2n;
954 uchar cb;
955 CMYKColor cmykCol;
956 QVector<int> curveTable1;
957 QVector<int> curveTable2;
958 QVector<int> curveTable3;
959 ScColorEngine::getShadeColorCMYK(color1, doc, cmykCol, shade1);
960 cmykCol.getValues(c, m, y, k);
961 ScColorEngine::getShadeColorCMYK(color2, doc, cmykCol, shade2);
962 cmykCol.getValues(c1, m1, y1, k1);
963 ScColorEngine::getShadeColorCMYK(color3, doc, cmykCol, shade3);
964 cmykCol.getValues(c2, m2, y2, k2);
965 curveTable1.resize(256);
966 for (int x = 0 ; x < 256 ; x++)
967 {
968 curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve1, x / 255.0, lin1) * 255)));
969 }
970 curveTable2.resize(256);
971 for (int x = 0 ; x < 256 ; x++)
972 {
973 curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin2) * 255)));
974 }
975 curveTable3.resize(256);
976 for (int x = 0 ; x < 256 ; x++)
977 {
978 curveTable3[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin3) * 255)));
979 }
980 for (int yi = 0; yi < h; ++yi)
981 {
982 QRgb * s = (QRgb*)(scanLine( yi ));
983 for (int xi = 0; xi < w; ++xi)
984 {
985 QRgb r = *s;
986 if (cmyk)
987 cb = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
988 else
989 cb = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
990 cn = qMin((c * curveTable1[(int)cb]) >> 8, 255);
991 mn = qMin((m * curveTable1[(int)cb]) >> 8, 255);
992 yn = qMin((y * curveTable1[(int)cb]) >> 8, 255);
993 kn = qMin((k * curveTable1[(int)cb]) >> 8, 255);
994 c1n = qMin((c1 * curveTable2[(int)cb]) >> 8, 255);
995 m1n = qMin((m1 * curveTable2[(int)cb]) >> 8, 255);
996 y1n = qMin((y1 * curveTable2[(int)cb]) >> 8, 255);
997 k1n = qMin((k1 * curveTable2[(int)cb]) >> 8, 255);
998 c2n = qMin((c2 * curveTable3[(int)cb]) >> 8, 255);
999 m2n = qMin((m2 * curveTable3[(int)cb]) >> 8, 255);
1000 y2n = qMin((y2 * curveTable3[(int)cb]) >> 8, 255);
1001 k2n = qMin((k2 * curveTable3[(int)cb]) >> 8, 255);
1002 ScColor col = ScColor(qMin(cn+c1n+c2n, 255), qMin(mn+m1n+m2n, 255), qMin(yn+y1n+y2n, 255), qMin(kn+k1n+k2n, 255));
1003 if (cmyk)
1004 col.getCMYK(&cn, &mn, &yn, &kn);
1005 else
1006 {
1007 col.getRawRGBColor(&cn, &mn, &yn);
1008 kn = qAlpha(r);
1009 }
1010 *s = qRgba(cn, mn, yn, kn);
1011 s++;
1012 }
1013 }
1014 }
1015
quadtone(ScribusDoc * doc,ScColor color1,int shade1,FPointArray curve1,bool lin1,ScColor color2,int shade2,FPointArray curve2,bool lin2,ScColor color3,int shade3,FPointArray curve3,bool lin3,ScColor color4,int shade4,FPointArray curve4,bool lin4,bool cmyk)1016 void ScImage::quadtone(ScribusDoc* doc, ScColor color1, int shade1, FPointArray curve1, bool lin1, ScColor color2, int shade2, FPointArray curve2, bool lin2, ScColor color3, int shade3, FPointArray curve3, bool lin3, ScColor color4, int shade4, FPointArray curve4, bool lin4, bool cmyk)
1017 {
1018 int h = height();
1019 int w = width();
1020 int c, c1, c2, c3, m, m1, m2, m3, y, y1, y2, y3, k, k1, k2, k3;
1021 int cn, c1n, c2n, c3n, mn, m1n, m2n, m3n, yn, y1n, y2n, y3n, kn, k1n, k2n, k3n;
1022 uchar cb;
1023 CMYKColor cmykCol;
1024 QVector<int> curveTable1;
1025 QVector<int> curveTable2;
1026 QVector<int> curveTable3;
1027 QVector<int> curveTable4;
1028 ScColorEngine::getShadeColorCMYK(color1, doc, cmykCol, shade1);
1029 cmykCol.getValues(c, m, y, k);
1030 ScColorEngine::getShadeColorCMYK(color2, doc, cmykCol, shade2);
1031 cmykCol.getValues(c1, m1, y1, k1);
1032 ScColorEngine::getShadeColorCMYK(color3, doc, cmykCol, shade3);
1033 cmykCol.getValues(c2, m2, y2, k2);
1034 ScColorEngine::getShadeColorCMYK(color4, doc, cmykCol, shade4);
1035 cmykCol.getValues(c3, m3, y3, k3);
1036 curveTable1.resize(256);
1037 for (int x = 0 ; x < 256 ; x++)
1038 {
1039 curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve1, x / 255.0, lin1) * 255)));
1040 }
1041 curveTable2.resize(256);
1042 for (int x = 0 ; x < 256 ; x++)
1043 {
1044 curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin2) * 255)));
1045 }
1046 curveTable3.resize(256);
1047 for (int x = 0 ; x < 256 ; x++)
1048 {
1049 curveTable3[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve3, x / 255.0, lin3) * 255)));
1050 }
1051 curveTable4.resize(256);
1052 for (int x = 0 ; x < 256 ; x++)
1053 {
1054 curveTable4[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve4, x / 255.0, lin4) * 255)));
1055 }
1056 for (int yi=0; yi < h; ++yi)
1057 {
1058 QRgb * s = (QRgb*)(scanLine( yi ));
1059 for (int xi = 0; xi < w; ++xi)
1060 {
1061 QRgb r = *s;
1062 if (cmyk)
1063 cb = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
1064 else
1065 cb = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
1066 cn = qMin((c * curveTable1[(int)cb]) >> 8, 255);
1067 mn = qMin((m * curveTable1[(int)cb]) >> 8, 255);
1068 yn = qMin((y * curveTable1[(int)cb]) >> 8, 255);
1069 kn = qMin((k * curveTable1[(int)cb]) >> 8, 255);
1070 c1n = qMin((c1 * curveTable2[(int)cb]) >> 8, 255);
1071 m1n = qMin((m1 * curveTable2[(int)cb]) >> 8, 255);
1072 y1n = qMin((y1 * curveTable2[(int)cb]) >> 8, 255);
1073 k1n = qMin((k1 * curveTable2[(int)cb]) >> 8, 255);
1074 c2n = qMin((c2 * curveTable3[(int)cb]) >> 8, 255);
1075 m2n = qMin((m2 * curveTable3[(int)cb]) >> 8, 255);
1076 y2n = qMin((y2 * curveTable3[(int)cb]) >> 8, 255);
1077 k2n = qMin((k2 * curveTable3[(int)cb]) >> 8, 255);
1078 c3n = qMin((c3 * curveTable4[(int)cb]) >> 8, 255);
1079 m3n = qMin((m3 * curveTable4[(int)cb]) >> 8, 255);
1080 y3n = qMin((y3 * curveTable4[(int)cb]) >> 8, 255);
1081 k3n = qMin((k3 * curveTable4[(int)cb]) >> 8, 255);
1082 ScColor col = ScColor(qMin(cn+c1n+c2n+c3n, 255), qMin(mn+m1n+m2n+m3n, 255), qMin(yn+y1n+y2n+y3n, 255), qMin(kn+k1n+k2n+k3n, 255));
1083 if (cmyk)
1084 col.getCMYK(&cn, &mn, &yn, &kn);
1085 else
1086 {
1087 col.getRawRGBColor(&cn, &mn, &yn);
1088 kn = qAlpha(r);
1089 }
1090 *s = qRgba(cn, mn, yn, kn);
1091 s++;
1092 }
1093 }
1094 }
1095
invert(bool cmyk)1096 void ScImage::invert(bool cmyk)
1097 {
1098 int h = height();
1099 int w = width();
1100 unsigned char *p;
1101 QRgb * s;
1102 unsigned char c, m, y, k;
1103 for (int yi = 0; yi < h; ++yi)
1104 {
1105 s = (QRgb*)(scanLine( yi ));
1106 for (int xi = 0; xi < w; ++xi)
1107 {
1108 if (cmyk)
1109 {
1110 p = (unsigned char *) s;
1111 c = 255 - qMin(255, p[0] + p[3]);
1112 m = 255 - qMin(255, p[1] + p[3]);
1113 y = 255 - qMin(255, p[2] + p[3]);
1114 k = qMin(qMin(c, m), y);
1115 p[0] = c - k;
1116 p[1] = m - k;
1117 p[2] = y - k;
1118 p[3] = k;
1119 }
1120 else
1121 *s ^= 0x00ffffff;
1122 s++;
1123 }
1124 }
1125 }
1126
toGrayscale(bool cmyk)1127 void ScImage::toGrayscale(bool cmyk)
1128 {
1129 int h = height();
1130 int w = width();
1131 int k;
1132 QRgb * s;
1133 QRgb r;
1134 for (int yi = 0; yi < h; ++yi)
1135 {
1136 s = (QRgb*)(scanLine( yi ));
1137 for (int xi = 0; xi < w; ++xi)
1138 {
1139 r = *s;
1140 if (cmyk)
1141 {
1142 k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
1143 *s = qRgba(0, 0, 0, k);
1144 }
1145 else
1146 {
1147 k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
1148 *s = qRgba(k, k, k, qAlpha(r));
1149 }
1150 s++;
1151 }
1152 }
1153 }
1154
swapRGBA()1155 void ScImage::swapRGBA()
1156 {
1157 unsigned int *ptr;
1158 unsigned char *p;
1159 unsigned char r, b;
1160 for (int i = 0; i < height(); ++i)
1161 {
1162 ptr = (unsigned int *) scanLine(i);
1163 for (int j = 0; j < width(); ++j)
1164 {
1165 p = (unsigned char *) ptr;
1166 r = p[0];
1167 b = p[2];
1168 p[2] = r;
1169 p[0] = b;
1170 ptr++;
1171 }
1172 }
1173 }
1174
createLowRes(double scale)1175 bool ScImage::createLowRes(double scale)
1176 {
1177 int w = qRound(width() / scale);
1178 int h = qRound(height() / scale);
1179 if (w >= width() && h >= height()) // don't do unnecessary scaling
1180 return false;
1181 QImage tmp = scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1182 if (tmp.format() != QImage::Format_ARGB32)
1183 tmp = tmp.convertToFormat(QImage::Format_ARGB32);
1184 QImage::operator=(tmp);
1185 return true;
1186 }
1187
convert2JPG(const QString & fn,int Quality,bool isCMYK,bool isGray)1188 bool ScImage::convert2JPG(const QString& fn, int Quality, bool isCMYK, bool isGray)
1189 {
1190 QFile file(fn);
1191 if (!file.open(QIODevice::WriteOnly))
1192 return false;
1193
1194 bool success = false;
1195 ScJpegEncodeFilter::Color imgColor = ScJpegEncodeFilter::GRAY;
1196 if (isCMYK)
1197 imgColor = ScJpegEncodeFilter::CMYK;
1198 else if (!isGray)
1199 imgColor = ScJpegEncodeFilter::RGB;
1200 int qual[] = { 95, 85, 75, 50, 25 }; // These are the JPEG Quality settings 100 means best, 0 .. don't discuss
1201 QDataStream dataStream(&file);
1202 ScJpegEncodeFilter jpegFilter(&dataStream, width(), height(), imgColor);
1203 jpegFilter.setQuality(qual[Quality]);
1204 if (jpegFilter.openFilter())
1205 {
1206 if (isCMYK)
1207 success = writeCMYKDataToFilter(&jpegFilter);
1208 else if (isGray)
1209 success = writeGrayDataToFilter(&jpegFilter, true);
1210 else
1211 success = writeRGBDataToFilter(&jpegFilter);
1212 success &= jpegFilter.closeFilter();
1213 }
1214 file.close();
1215
1216 return success;
1217 }
1218
ImageToArray() const1219 QByteArray ScImage::ImageToArray() const
1220 {
1221 int i = 0;
1222 int h = height();
1223 int w = width();
1224 unsigned char u;
1225 const QRgb *rgb;
1226 QByteArray imgArray(3 * h * w, ' ');
1227 if (imgArray.isNull())
1228 return imgArray;
1229 for (int yi = 0; yi < h; ++yi)
1230 {
1231 rgb = (QRgb*) this->constScanLine(yi);
1232 for (int xi = 0; xi < w; ++xi)
1233 {
1234 u = qRed(*rgb);
1235 imgArray[i++] = u;
1236 u = qGreen(*rgb);
1237 imgArray[i++] = u;
1238 u = qBlue(*rgb);
1239 imgArray[i++] = u;
1240 ++rgb;
1241 }
1242 }
1243 return imgArray;
1244 }
1245
convertToGray()1246 void ScImage::convertToGray()
1247 {
1248 int k;
1249 int h = height();
1250 int w = width();
1251 QRgb *s, r;
1252 for (int yi = 0; yi < h; ++yi)
1253 {
1254 s = (QRgb*)(scanLine( yi ));
1255 for (int xi = 0; xi < w; ++xi)
1256 {
1257 r = *s;
1258 k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
1259 *s++ = qRgba(k, 0, 0, 0);
1260 }
1261 }
1262 }
1263
writeRGBDataToFilter(ScStreamFilter * filter) const1264 bool ScImage::writeRGBDataToFilter(ScStreamFilter* filter) const
1265 {
1266 QRgb r;
1267 const QRgb *s;
1268 QByteArray buffer;
1269 bool success = true;
1270 int h = height();
1271 int w = width();
1272 int pending = 0;
1273 int scanLineSize = (3 * w);
1274 int bufferSize = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
1275 buffer.resize(bufferSize + 16);
1276 if (buffer.isNull()) // Memory allocation failure
1277 return false;
1278 for (int yi = 0; yi < h; ++yi)
1279 {
1280 s = (const QRgb*) constScanLine(yi);
1281 for (int xi = 0; xi < w; ++xi)
1282 {
1283 r = *s++;
1284 buffer[pending++] = static_cast<unsigned char>(qRed(r));
1285 buffer[pending++] = static_cast<unsigned char>(qGreen(r));
1286 buffer[pending++] = static_cast<unsigned char>(qBlue(r));
1287 }
1288 if (pending >= bufferSize)
1289 {
1290 success &= filter->writeData(buffer.constData(), pending);
1291 pending = 0;
1292 }
1293 }
1294 if (pending)
1295 success &= filter->writeData(buffer.constData(), pending);
1296 return success;
1297 }
1298
writeGrayDataToFilter(ScStreamFilter * filter,bool precal) const1299 bool ScImage::writeGrayDataToFilter(ScStreamFilter* filter, bool precal) const
1300 {
1301 QRgb r;
1302 const QRgb *s;
1303 QByteArray buffer;
1304 bool success = true;
1305 int h = height();
1306 int w = width();
1307 int pending = 0, k;
1308 int scanLineSize = w;
1309 int bufferSize = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
1310 buffer.resize(bufferSize + 16);
1311 if (buffer.isNull()) // Memory allocation failure
1312 return false;
1313 for (int yi = 0; yi < h; ++yi)
1314 {
1315 s = (const QRgb*) constScanLine(yi);
1316 if (precal) // image data is already grayscale, no need for weighted conversion
1317 {
1318 for (int xi = 0; xi < w; ++xi)
1319 {
1320 r = *s;
1321 k = qRed(r);
1322 buffer[pending++] = k;
1323 s++;
1324 }
1325 }
1326 else
1327 {
1328 for (int xi = 0; xi < w; ++xi)
1329 {
1330 r = *s;
1331 k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
1332 buffer[pending++] = k;
1333 s++;
1334 }
1335 }
1336 if (pending >= bufferSize)
1337 {
1338 success &= filter->writeData(buffer.constData(), pending);
1339 pending = 0;
1340 }
1341 }
1342 if (pending)
1343 success &= filter->writeData(buffer.constData(), pending);
1344 return success;
1345 }
1346
writeMonochromeDataToFilter(ScStreamFilter * filter,bool fromCmyk) const1347 bool ScImage::writeMonochromeDataToFilter(ScStreamFilter* filter, bool fromCmyk) const
1348 {
1349 const QRgb *s;
1350 QByteArray buffer;
1351 bool success = true;
1352 int h = height();
1353 int w = width();
1354 int byteCount = 0;
1355 int bufferSize = (w + 7) / 8 * h;
1356 int value;
1357 const unsigned char threshold = 127;
1358 buffer.resize(bufferSize);
1359 if (buffer.isNull()) // Memory allocation failure
1360 return false;
1361 for (int yi = 0; yi < h; ++yi)
1362 {
1363 char curByte = 0;
1364 int bitCount = 0;
1365 s = (const QRgb*) constScanLine(yi);
1366 for (int xi = 0; xi < w; ++xi)
1367 {
1368 curByte <<= 1;
1369 value = fromCmyk ? (255 - qAlpha(*s)) : qRed(*s);
1370 if (value > threshold) // In monochrome images all elements have the same value.
1371 curByte |= 1;
1372 ++bitCount;
1373 if (bitCount == 8)
1374 {
1375 buffer[byteCount++] = curByte;
1376 curByte = 0;
1377 bitCount = 0;
1378 }
1379 ++s;
1380 }
1381 // End of line is aligned to byte.
1382 if (bitCount > 0) {
1383 curByte <<= 8-bitCount;
1384 buffer[byteCount++] = curByte;
1385 }
1386 }
1387 assert(byteCount == bufferSize);
1388 success = filter->writeData(buffer.constData(), byteCount);
1389 return success;
1390 }
1391
writeCMYKDataToFilter(ScStreamFilter * filter) const1392 bool ScImage::writeCMYKDataToFilter(ScStreamFilter* filter) const
1393 {
1394 QRgb r;
1395 const QRgb *s;
1396 QByteArray buffer;
1397 bool success = true;
1398 int h = height();
1399 int w = width();
1400 int pending = 0;
1401 int scanLineSize = (4 * w);
1402 int bufferSize = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
1403 buffer.resize(bufferSize + 16);
1404 if (buffer.isNull()) // Memory allocation failure
1405 return false;
1406 for (int yi = 0; yi < h; ++yi)
1407 {
1408 s = (const QRgb*) constScanLine(yi);
1409 for (int xi = 0; xi < w; ++xi)
1410 {
1411 r = *s++;
1412 buffer[pending++] = static_cast<unsigned char> (qRed(r));
1413 buffer[pending++] = static_cast<unsigned char> (qGreen(r));
1414 buffer[pending++] = static_cast<unsigned char> (qBlue(r));
1415 buffer[pending++] = static_cast<unsigned char> (qAlpha(r));
1416 }
1417 if (pending >= bufferSize)
1418 {
1419 success &= filter->writeData(buffer.constData(), pending);
1420 pending = 0;
1421 }
1422 }
1423 if (pending)
1424 success &= filter->writeData(buffer.constData(), pending);
1425 return success;
1426 }
1427
writePSImageToFilter(ScStreamFilter * filter,int pl) const1428 bool ScImage::writePSImageToFilter(ScStreamFilter* filter, int pl) const
1429 {
1430 QRgb r;
1431 const QRgb *s;
1432 QByteArray buffer;
1433 bool success = true;
1434 int c, m, y, k;
1435 int h = height();
1436 int w = width();
1437 int pending = 0;
1438 int scanLineSize = (pl == -1) ? (4 * w) : w;
1439 int bufferSize = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
1440 buffer.resize(bufferSize + 16);
1441 if (buffer.isNull()) // Memory allocation failure
1442 return false;
1443 for (int yi = 0; yi < h; ++yi)
1444 {
1445 s = (const QRgb*) constScanLine(yi);
1446 for (int xi = 0; xi < w; ++xi)
1447 {
1448 r = *s++;
1449 c = qRed(r);
1450 m = qGreen(r);
1451 y = qBlue(r);
1452 k = qAlpha(r);
1453 if (pl == -1)
1454 {
1455 buffer[pending++] = static_cast<unsigned char> (c);
1456 buffer[pending++] = static_cast<unsigned char> (m);
1457 buffer[pending++] = static_cast<unsigned char> (y);
1458 buffer[pending++] = static_cast<unsigned char> (k);
1459 }
1460 else
1461 {
1462 if (pl == -2)
1463 buffer[pending++] = static_cast<unsigned char> (qMin(255, qRound(0.3 * c + 0.59 * m + 0.11 * y + k)));
1464 if (pl == 1)
1465 buffer[pending++] = static_cast<unsigned char> (c);
1466 if (pl == 2)
1467 buffer[pending++] = static_cast<unsigned char> (m);
1468 if (pl == 3)
1469 buffer[pending++] = static_cast<unsigned char> (y);
1470 if (pl == 0)
1471 buffer[pending++] = static_cast<unsigned char> (k);
1472 }
1473 }
1474 if (pending >= bufferSize)
1475 {
1476 success &= filter->writeData(buffer.constData(), pending);
1477 pending = 0;
1478 }
1479 }
1480 if (pending)
1481 success &= filter->writeData(buffer.constData(), pending);
1482 return success;
1483 }
1484
writePSImageToFilter(ScStreamFilter * filter,const QByteArray & mask,int pl) const1485 bool ScImage::writePSImageToFilter(ScStreamFilter* filter, const QByteArray& mask, int pl) const
1486 {
1487 QRgb r;
1488 const QRgb *s;
1489 QByteArray buffer;
1490 bool success = true;
1491 int c, m, y, k;
1492 int h = height();
1493 int w = width();
1494 int pending = 0;
1495 int scanLineSize = (pl == -1) ? (5 * w) : (2 * w);
1496 int bufferSize = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
1497 if (mask.size() < (h * w)) // Check if mask provide enough data
1498 return false;
1499 buffer.resize(bufferSize + 16);
1500 if (buffer.isNull()) // Check for memory allocation failure
1501 return false;
1502 unsigned char* maskData = (unsigned char*) mask.constData();
1503 for (int yi = 0; yi < h; ++yi)
1504 {
1505 s = (const QRgb*) constScanLine(yi);
1506 for (int xi = 0; xi < w; ++xi)
1507 {
1508 r = *s++;
1509 c = qRed(r);
1510 m = qGreen(r);
1511 y = qBlue(r);
1512 k = qAlpha(r);
1513 if (pl == -1)
1514 {
1515 buffer[pending++] = *maskData++;
1516 buffer[pending++] = static_cast<unsigned char> (c);
1517 buffer[pending++] = static_cast<unsigned char> (m);
1518 buffer[pending++] = static_cast<unsigned char> (y);
1519 buffer[pending++] = static_cast<unsigned char> (k);
1520 }
1521 else
1522 {
1523 buffer[pending++] = *maskData++;
1524 if (pl == -2)
1525 buffer[pending++] = static_cast<unsigned char> (qMin(255, qRound(0.3 * c + 0.59 * m + 0.11 * y + k)));
1526 if (pl == 1)
1527 buffer[pending++] = static_cast<unsigned char> (c);
1528 if (pl == 2)
1529 buffer[pending++] = static_cast<unsigned char> (m);
1530 if (pl == 3)
1531 buffer[pending++] = static_cast<unsigned char> (y);
1532 if (pl == 0)
1533 buffer[pending++] = static_cast<unsigned char> (k);
1534 }
1535 }
1536 if (pending >= bufferSize)
1537 {
1538 success &= filter->writeData(buffer.constData(), pending);
1539 pending = 0;
1540 }
1541 }
1542 if (pending)
1543 success &= filter->writeData(buffer.constData(), pending);
1544 return success;
1545 }
1546
scaleImage(int nwidth,int nheight)1547 void ScImage::scaleImage(int nwidth, int nheight)
1548 {
1549 int depth = this->depth();
1550 if (depth == 32)
1551 {
1552 scaleImage32bpp(nwidth, nheight);
1553 return;
1554 }
1555 scaleImageGeneric(nwidth, nheight);
1556 }
1557
scaleImage32bpp(int nwidth,int nheight)1558 void ScImage::scaleImage32bpp(int nwidth, int nheight)
1559 {
1560 QImage dst(nwidth, nheight, QImage::Format_ARGB32);
1561 QRgb* xelrow = nullptr;
1562 QRgb* tempxelrow = nullptr;
1563 QRgb* xP = nullptr;
1564 QRgb* nxP = nullptr;
1565 int rows, cols, rowsread, newrows, newcols;
1566 int row, col, needtoreadrow;
1567 const uchar maxval = 255;
1568 double xscale, yscale;
1569 long sxscale, syscale;
1570 long fracrowtofill, fracrowleft;
1571 long* as = nullptr;
1572 long* rs = nullptr;
1573 long* gs = nullptr;
1574 long* bs = nullptr;
1575 int rowswritten = 0;
1576
1577 int depth = this->depth();
1578 if (depth != 32)
1579 {
1580 QImage::operator=(QImage::scaled(nwidth, nheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
1581 return;
1582 }
1583
1584 cols = width();
1585 rows = height();
1586 newcols = dst.width();
1587 newrows = dst.height();
1588 long SCALE;
1589 long HALFSCALE;
1590 if (cols > 4096)
1591 {
1592 SCALE = 4096;
1593 HALFSCALE = 2048;
1594 }
1595 else
1596 {
1597 int fac = 4096;
1598 while ((cols * fac) > 4096)
1599 {
1600 fac /= 2;
1601 }
1602 SCALE = fac * cols;
1603 HALFSCALE = fac * cols / 2;
1604 }
1605 xscale = (double) newcols / (double) cols;
1606 yscale = (double) newrows / (double) rows;
1607 sxscale = (long) (xscale * SCALE);
1608 syscale = (long) (yscale * SCALE);
1609 if ( newrows != rows ) /* shortcut Y scaling if possible */
1610 tempxelrow = new QRgb[cols];
1611 as = new long[cols];
1612 rs = new long[cols];
1613 gs = new long[cols];
1614 bs = new long[cols];
1615 rowsread = 0;
1616 fracrowleft = syscale;
1617 needtoreadrow = 1;
1618 for (col = 0; col < cols; ++col)
1619 rs[col] = gs[col] = as[col] = bs[col] = HALFSCALE;
1620 fracrowtofill = SCALE;
1621 for (row = 0; row < newrows; ++row)
1622 {
1623 if ( newrows == rows )
1624 tempxelrow = xelrow = (QRgb*) scanLine(rowsread++);
1625 else
1626 {
1627 while ( fracrowleft < fracrowtofill )
1628 {
1629 if ( needtoreadrow && rowsread < rows )
1630 xelrow = (QRgb*) scanLine(rowsread++);
1631 for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
1632 {
1633 as[col] += fracrowleft * qAlpha( *xP );
1634 rs[col] += fracrowleft * qRed( *xP );
1635 gs[col] += fracrowleft * qGreen( *xP );
1636 bs[col] += fracrowleft * qBlue( *xP );
1637 }
1638 fracrowtofill -= fracrowleft;
1639 fracrowleft = syscale;
1640 needtoreadrow = 1;
1641 }
1642 if (needtoreadrow && rowsread < rows)
1643 {
1644 xelrow = (QRgb*) scanLine(rowsread++);
1645 needtoreadrow = 0;
1646 }
1647 long a = 0;
1648 for (col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col, ++xP, ++nxP)
1649 {
1650 long r, g, b;
1651 a = as[col] + fracrowtofill * qAlpha( *xP );
1652 r = rs[col] + fracrowtofill * qRed( *xP );
1653 g = gs[col] + fracrowtofill * qGreen( *xP );
1654 b = bs[col] + fracrowtofill * qBlue( *xP );
1655 r /= SCALE;
1656 if (r > maxval)
1657 r = maxval;
1658 g /= SCALE;
1659 if (g > maxval)
1660 g = maxval;
1661 b /= SCALE;
1662 if (b > maxval)
1663 b = maxval;
1664 a /= SCALE;
1665 if (a > maxval)
1666 a = maxval;
1667 *nxP = qRgba((int) r, (int) g, (int) b , (int) a);
1668 rs[col] = as[col] = gs[col] = bs[col] = HALFSCALE;
1669 }
1670 fracrowleft -= fracrowtofill;
1671 if (fracrowleft == 0)
1672 {
1673 fracrowleft = syscale;
1674 needtoreadrow = 1;
1675 }
1676 fracrowtofill = SCALE;
1677 }
1678 if (newcols == cols)
1679 memcpy(dst.scanLine(rowswritten++), tempxelrow, newcols*4);
1680 else
1681 {
1682 long a, r, g, b;
1683 long fraccoltofill, fraccolleft = 0;
1684 int needcol;
1685 nxP = (QRgb*) dst.scanLine(rowswritten++);
1686 QRgb *nxPEnd = nxP + newcols;
1687 fraccoltofill = SCALE;
1688 a = r = g = b = HALFSCALE;
1689 needcol = 0;
1690 for (col = 0, xP = tempxelrow; col < cols; ++col, ++xP)
1691 {
1692 fraccolleft = sxscale;
1693 while (fraccolleft >= fraccoltofill)
1694 {
1695 if (needcol)
1696 {
1697 ++nxP;
1698 a = r = g = b = HALFSCALE;
1699 }
1700 a += fraccoltofill * qAlpha(*xP);
1701 r += fraccoltofill * qRed(*xP);
1702 g += fraccoltofill * qGreen(*xP);
1703 b += fraccoltofill * qBlue(*xP);
1704 r /= SCALE;
1705 if (r > maxval)
1706 r = maxval;
1707 g /= SCALE;
1708 if (g > maxval)
1709 g = maxval;
1710 b /= SCALE;
1711 if (b > maxval)
1712 b = maxval;
1713 a /= SCALE;
1714 if (a > maxval)
1715 a = maxval;
1716 *nxP = qRgba((int) r, (int) g, (int) b, (int) a);
1717 fraccolleft -= fraccoltofill;
1718 fraccoltofill = SCALE;
1719 needcol = 1;
1720 }
1721 if (fraccolleft > 0)
1722 {
1723 if (needcol)
1724 {
1725 ++nxP;
1726 a = r = g = b = HALFSCALE;
1727 needcol = 0;
1728 }
1729 a += fraccolleft * qAlpha(*xP);
1730 r += fraccolleft * qRed(*xP);
1731 g += fraccolleft * qGreen(*xP);
1732 b += fraccolleft * qBlue(*xP);
1733 fraccoltofill -= fraccolleft;
1734 }
1735 }
1736 if (fraccoltofill > 0)
1737 {
1738 --xP;
1739 a += fraccoltofill * qAlpha(*xP);
1740 r += fraccoltofill * qRed(*xP);
1741 g += fraccoltofill * qGreen(*xP);
1742 b += fraccoltofill * qBlue(*xP);
1743 }
1744 if (nxP < nxPEnd)
1745 {
1746 r /= SCALE;
1747 if (r > maxval)
1748 r = maxval;
1749 g /= SCALE;
1750 if (g > maxval)
1751 g = maxval;
1752 b /= SCALE;
1753 if (b > maxval)
1754 b = maxval;
1755 a /= SCALE;
1756 if (a > maxval)
1757 a = maxval;
1758 *nxP = qRgba((int) r, (int) g, (int) b, (int) a);
1759 while (++nxP != nxPEnd)
1760 nxP[0] = nxP[-1];
1761 }
1762 }
1763 }
1764 if (newrows != rows && tempxelrow) // Robust, tempxelrow might be 0 1 day
1765 delete [] tempxelrow;
1766 delete [] as;
1767 delete [] rs;
1768 delete [] gs;
1769 delete [] bs;
1770 QImage::operator=(QImage(nwidth, nheight, QImage::Format_ARGB32));
1771 for (int yi = 0; yi < dst.height(); ++yi)
1772 {
1773 QRgb *s = (QRgb*) dst.scanLine(yi);
1774 QRgb *d = (QRgb*) scanLine(yi);
1775 for (int xi = 0; xi < dst.width(); ++xi)
1776 {
1777 (*d) = (*s);
1778 s++;
1779 d++;
1780 }
1781 }
1782 }
1783
scaleImageGeneric(int nwidth,int nheight)1784 void ScImage::scaleImageGeneric(int nwidth, int nheight)
1785 {
1786 unsigned char* xelrow = nullptr;
1787 unsigned char* tempxelrow = nullptr;
1788 unsigned char* xP;
1789 unsigned char* nxP;
1790 int rows, cols, rowsread, newrows, newcols;
1791 int row, col, needtoreadrow;
1792 const uchar maxval = 255;
1793 double xscale, yscale;
1794 long sxscale, syscale;
1795 long fracrowtofill, fracrowleft;
1796 long* ps;
1797 int rowswritten = 0;
1798
1799 int depth = this->depth();
1800 Format imgFormat = this->format();
1801 bool execScaled = (depth == 1 || depth == 4 || depth == 16);
1802 execScaled |= (imgFormat == QImage::Format_ARGB8565_Premultiplied);
1803 execScaled |= (imgFormat == QImage::Format_RGB666);
1804 execScaled |= (imgFormat == QImage::Format_ARGB6666_Premultiplied);
1805 execScaled |= (imgFormat == QImage::Format_ARGB8555_Premultiplied);
1806 if (execScaled)
1807 {
1808 QImage::operator=(QImage::scaled(nwidth, nheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
1809 return;
1810 }
1811
1812 QImage dst(nwidth, nheight, this->format());
1813 int nChannels = this->depth() / 8;
1814
1815 cols = width();
1816 rows = height();
1817 newcols = dst.width();
1818 newrows = dst.height();
1819 long SCALE;
1820 long HALFSCALE;
1821 if (cols > 4096)
1822 {
1823 SCALE = 4096;
1824 HALFSCALE = 2048;
1825 }
1826 else
1827 {
1828 int fac = 4096;
1829 while ((cols * fac) > 4096)
1830 {
1831 fac /= 2;
1832 }
1833 SCALE = fac * cols;
1834 HALFSCALE = fac * cols / 2;
1835 }
1836 xscale = (double) newcols / (double) cols;
1837 yscale = (double) newrows / (double) rows;
1838 sxscale = (long)(xscale * SCALE);
1839 syscale = (long)(yscale * SCALE);
1840 if (newrows != rows) /* shortcut Y scaling if possible */
1841 tempxelrow = new unsigned char[cols * nChannels];
1842 ps = new long[cols];
1843
1844 for (int chIndex = 0; chIndex < nChannels; ++chIndex)
1845 {
1846 xelrow = nullptr;
1847 rowsread = rowswritten = 0;
1848 fracrowleft = syscale;
1849 needtoreadrow = 1;
1850 for (col = 0; col < cols; ++col)
1851 ps[col] = HALFSCALE;
1852 fracrowtofill = SCALE;
1853 for (row = 0; row < newrows; ++row)
1854 {
1855 if (newrows == rows)
1856 tempxelrow = xelrow = scanLine(rowsread++);
1857 else
1858 {
1859 while (fracrowleft < fracrowtofill)
1860 {
1861 if (needtoreadrow && rowsread < rows)
1862 xelrow = scanLine(rowsread++);
1863 for (col = 0, xP = xelrow + chIndex; col < cols; ++col, xP += nChannels)
1864 ps[col] += fracrowleft * (*xP);
1865 fracrowtofill -= fracrowleft;
1866 fracrowleft = syscale;
1867 needtoreadrow = 1;
1868 }
1869 if (needtoreadrow && rowsread < rows)
1870 {
1871 xelrow = scanLine(rowsread++);
1872 needtoreadrow = 0;
1873 }
1874 long p = 0;
1875 xP = xelrow + chIndex;
1876 nxP = tempxelrow + chIndex;
1877 for (col = 0; col < cols; ++col, xP += nChannels, nxP += nChannels)
1878 {
1879 p = ps[col] + fracrowtofill * (*xP);
1880 p /= SCALE;
1881 if (p > maxval)
1882 p = maxval;
1883 *nxP = (unsigned char) p;
1884 ps[col] = HALFSCALE;
1885 }
1886 fracrowleft -= fracrowtofill;
1887 if (fracrowleft == 0)
1888 {
1889 fracrowleft = syscale;
1890 needtoreadrow = 1;
1891 }
1892 fracrowtofill = SCALE;
1893 }
1894 if (newcols == cols)
1895 {
1896 memcpy(dst.scanLine(rowswritten++), tempxelrow, newcols * nChannels);
1897 }
1898 else
1899 {
1900 long p;
1901 long fraccoltofill, fraccolleft = 0;
1902 int needcol;
1903 nxP = dst.scanLine(rowswritten++) + chIndex;
1904 unsigned char *nxPEnd = nxP + newcols * nChannels;
1905 fraccoltofill = SCALE;
1906 p = HALFSCALE;
1907 needcol = 0;
1908 for (col = 0, xP = tempxelrow + chIndex; col < cols; ++col, xP += nChannels)
1909 {
1910 fraccolleft = sxscale;
1911 while (fraccolleft >= fraccoltofill)
1912 {
1913 if (needcol)
1914 {
1915 nxP += nChannels;
1916 p = HALFSCALE;
1917 }
1918 p += fraccoltofill * (*xP);
1919 p /= SCALE;
1920 if (p > maxval)
1921 p = maxval;
1922 *nxP = (unsigned char) p;
1923 fraccolleft -= fraccoltofill;
1924 fraccoltofill = SCALE;
1925 needcol = 1;
1926 }
1927 if (fraccolleft > 0)
1928 {
1929 if (needcol)
1930 {
1931 nxP += nChannels;
1932 p = HALFSCALE;
1933 needcol = 0;
1934 }
1935 p += fraccolleft * (*xP);
1936 fraccoltofill -= fraccolleft;
1937 }
1938 }
1939 if (fraccoltofill > 0)
1940 {
1941 xP -= nChannels;
1942 p += fraccoltofill * (*xP);
1943 }
1944 if (nxP < nxPEnd)
1945 {
1946 p /= SCALE;
1947 if (p > maxval)
1948 p = maxval;
1949 *nxP = (unsigned char) p;
1950 while ((nxP += nChannels) != nxPEnd)
1951 nxP[0] = nxP[-nChannels];
1952 }
1953 }
1954 }
1955 }
1956 if (newrows != rows && tempxelrow)// Robust, tempxelrow might be 0 1 day
1957 delete [] tempxelrow;
1958 delete [] ps;
1959
1960 int scanWidth = dst.width() * nChannels;
1961 QImage::operator=(QImage(nwidth, nheight, this->format()));
1962 for (int yi = 0; yi < dst.height(); ++yi)
1963 {
1964 uchar *s = dst.scanLine(yi);
1965 uchar *d = scanLine(yi);
1966 memcpy(d, s, scanWidth);
1967 }
1968 }
1969
getAlpha(const QString & fn,int page,QByteArray & alpha,bool PDF,bool pdf14,int gsRes,int scaleXSize,int scaleYSize)1970 bool ScImage::getAlpha(const QString& fn, int page, QByteArray& alpha, bool PDF, bool pdf14, int gsRes, int scaleXSize, int scaleYSize)
1971 {
1972 bool gotAlpha = false;
1973 QScopedPointer<ScImgDataLoader> pDataLoader;
1974 imgInfo.valid = false;
1975 imgInfo.clipPath.clear();
1976 imgInfo.PDSpathData.clear();
1977 imgInfo.layerInfo.clear();
1978 QFileInfo fi = QFileInfo(fn);
1979 if (!fi.exists())
1980 return false;
1981 alpha.resize(0);
1982 QString ext = fi.suffix().toLower();
1983 QString ext2 = getImageType(fn);
1984 if (ext.isEmpty() || (!ext2.isEmpty() && (ext2 != ext)))
1985 ext = ext2;
1986 QList<QByteArray> fmtList = QImageReader::supportedImageFormats();
1987 QStringList fmtImg;
1988 for (int i = 0; i < fmtList.count(); i++)
1989 {
1990 fmtImg.append( QString(fmtList[i].toLower()) );
1991 }
1992 if (extensionIndicatesJPEG(ext))
1993 return true;
1994 if (extensionIndicatesPDF(ext))
1995 {
1996 pDataLoader.reset( new ScImgDataLoader_PDF() );
1997 }
1998 else if (extensionIndicatesEPSorPS(ext))
1999 {
2000 pDataLoader.reset( new ScImgDataLoader_PS() );
2001 }
2002 else if (extensionIndicatesPNG(ext))
2003 {
2004 pDataLoader.reset( new ScImgDataLoader_PNG() );
2005 if (pDataLoader.data())
2006 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2007 }
2008 else if (extensionIndicatesPSD(ext))
2009 {
2010 pDataLoader.reset( new ScImgDataLoader_PSD() );
2011 if (pDataLoader.data())
2012 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2013 }
2014 else if (extensionIndicatesTIFF(ext))
2015 {
2016 pDataLoader.reset( new ScImgDataLoader_TIFF() );
2017 if (pDataLoader.data())
2018 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2019 }
2020 else if (ext == "ora")
2021 {
2022 pDataLoader.reset( new ScImgDataLoader_ORA() );
2023 if (pDataLoader.data())
2024 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2025 }
2026 else if (ext == "kra")
2027 {
2028 pDataLoader.reset( new ScImgDataLoader_KRA() );
2029 if (pDataLoader.data())
2030 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2031 }
2032 else if (ext == "pat")
2033 pDataLoader.reset( new ScImgDataLoader_GIMP() );
2034 else if (ext == "pgf")
2035 pDataLoader.reset( new ScImgDataLoader_PGF() );
2036 else if ((ext == "pct") || (ext == "pic") || (ext == "pict"))
2037 pDataLoader.reset( new ScImgDataLoader_PICT() );
2038 else if (ext == "wpg")
2039 pDataLoader.reset( new ScImgDataLoader_WPG() );
2040 #ifdef GMAGICK_FOUND
2041 #warning "Compiling with GraphicsMagick support!"
2042 else if (fmtImg.contains(ext))
2043 pDataLoader.reset( new ScImgDataLoader_QT() );
2044 else
2045 pDataLoader.reset( new ScImgDataLoader_GMagick() );
2046 #else
2047 else
2048 pDataLoader.reset( new ScImgDataLoader_QT() );
2049 #endif
2050
2051 if (pDataLoader.data())
2052 {
2053 bool hasAlpha = false;
2054 bool alphaLoaded = pDataLoader->preloadAlphaChannel(fn, page, gsRes, hasAlpha);
2055 if (!alphaLoaded || !hasAlpha)
2056 return alphaLoaded;
2057 QImage rImage;
2058 if (pDataLoader->useRawImage() || extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext))
2059 {
2060 if (pDataLoader->imageInfoRecord().valid)
2061 {
2062 if ((pDataLoader->r_image.channels() == 5) || (pDataLoader->imageInfoRecord().colorspace == ColorSpaceCMYK))
2063 rImage = pDataLoader->r_image.convertToQImage(true);
2064 else
2065 rImage = pDataLoader->r_image.convertToQImage(false);
2066 }
2067 }
2068 else
2069 rImage = pDataLoader->image();
2070 if (rImage.isNull())
2071 return false;
2072 if ((scaleXSize != 0) && (scaleYSize != 0) && (scaleXSize != rImage.width() || scaleYSize != rImage.height()))
2073 rImage = rImage.scaled(scaleXSize, scaleYSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2074 int i = 0, w2;
2075 unsigned char u;
2076 int hm = rImage.height();
2077 int wm = rImage.width();
2078 QRgb r, *s;
2079 if (pdf14)
2080 {
2081 alpha.resize(hm * wm);
2082 if (alpha.size() > 0) //
2083 {
2084 for (int yi = 0; yi < hm; ++yi)
2085 {
2086 s = (QRgb*) rImage.scanLine(yi);
2087 for (int xi = 0; xi < wm; ++xi)
2088 {
2089 r = *s++;
2090 u = qAlpha(r);
2091 alpha[i++] = u;
2092 }
2093 }
2094 gotAlpha = true;
2095 }
2096 }
2097 else
2098 {
2099 uchar * s;
2100 QImage iMask = rImage.createAlphaMask();
2101 iMask = iMask.convertToFormat(QImage::Format_Mono);
2102 hm = iMask.height();
2103 wm = iMask.width();
2104 w2 = wm / 8;
2105 if ((wm % 8) != 0)
2106 w2++;
2107 alpha.resize(hm * w2);
2108 if (alpha.size() > 0)
2109 {
2110 for (int yi = 0; yi < hm; ++yi)
2111 {
2112 s = iMask.scanLine( yi );
2113 for (int xi = 0; xi < w2; ++xi)
2114 {
2115 u = *(s + xi);
2116 if (PDF) u = ~u;
2117 alpha[i++] = u;
2118 }
2119 }
2120 gotAlpha = true;
2121 }
2122 }
2123 }
2124 return gotAlpha;
2125 }
2126
getEmbeddedProfile(const QString & fn,QByteArray * profile,int * components,int page)2127 void ScImage::getEmbeddedProfile(const QString & fn, QByteArray *profile, int *components, int page)
2128 {
2129 Q_ASSERT(profile);
2130 Q_ASSERT(components);
2131 ScColorProfile prof;
2132 ScImgDataLoader* pDataLoader = nullptr;
2133
2134 profile->resize(0);
2135 *components = 0;
2136
2137 QFileInfo fi = QFileInfo(fn);
2138 if (!fi.exists())
2139 return;
2140 QString ext = fi.suffix().toLower();
2141 QString ext2 = getImageType(fn);
2142 if (ext.isEmpty() || (!ext2.isEmpty() && (ext2 != ext)))
2143 ext = ext2;
2144
2145 QList<QByteArray> fmtList = QImageReader::supportedImageFormats();
2146 QStringList fmtImg;
2147 for (int i = 0; i < fmtList.count(); i++)
2148 fmtImg.append( QString(fmtList[i].toLower()) );
2149
2150 if (extensionIndicatesPSD(ext))
2151 pDataLoader = new ScImgDataLoader_PSD();
2152 else if (extensionIndicatesEPSorPS(ext))
2153 pDataLoader = new ScImgDataLoader_PS();
2154 else if (extensionIndicatesJPEG(ext))
2155 pDataLoader = new ScImgDataLoader_JPEG();
2156 else if (extensionIndicatesPNG(ext))
2157 pDataLoader = new ScImgDataLoader_PNG();
2158 else if (extensionIndicatesTIFF(ext))
2159 pDataLoader = new ScImgDataLoader_TIFF();
2160 #ifdef GMAGICK_FOUND
2161 else if (fmtImg.contains(ext))
2162 pDataLoader = new ScImgDataLoader_QT();
2163 else
2164 pDataLoader = new ScImgDataLoader_GMagick();
2165 #else
2166 else
2167 pDataLoader = new ScImgDataLoader_QT();
2168 #endif
2169
2170 if (pDataLoader)
2171 {
2172 pDataLoader->loadEmbeddedProfile(fn, page);
2173 QByteArray embeddedProfile = pDataLoader->embeddedProfile();
2174 if (!embeddedProfile.isEmpty())
2175 {
2176 prof = ScCore->defaultEngine.openProfileFromMem(embeddedProfile);
2177 if (prof)
2178 {
2179 if (prof.colorSpace() == ColorSpace_Rgb)
2180 *components = 3;
2181 if (prof.colorSpace() == ColorSpace_Cmyk)
2182 *components = 4;
2183 if (prof.colorSpace() == ColorSpace_Gray)
2184 *components = 1;
2185 *profile = embeddedProfile;
2186 }
2187 }
2188 delete pDataLoader;
2189 }
2190 }
2191
addProfileToCacheModifiers(ScImageCacheProxy & cache,const QString & prefix,const ScColorProfile & profile) const2192 void ScImage::addProfileToCacheModifiers(ScImageCacheProxy & cache, const QString & prefix, const ScColorProfile & profile) const
2193 {
2194 if (profile)
2195 {
2196 cache.addModifier(prefix + "ProfileDescription", profile.productDescription());
2197 QString hash = profile.dataHash();
2198 if (!hash.isEmpty())
2199 cache.addModifier(prefix + "ProfileHash", hash);
2200 }
2201 }
2202
loadPicture(ScImageCacheProxy & cache,bool & fromCache,int page,const CMSettings & cmSettings,RequestType requestType,int gsRes,bool * realCMYK,bool showMsg)2203 bool ScImage::loadPicture(ScImageCacheProxy & cache, bool & fromCache, int page, const CMSettings& cmSettings,
2204 RequestType requestType, int gsRes, bool *realCMYK, bool showMsg)
2205 {
2206 if (cache.enabled())
2207 {
2208 ScColorMgmtEngine engine(cmSettings.doc() ? cmSettings.doc()->colorEngine : ScCore->defaultEngine);
2209 cache.addModifier("cmEngineID", QString::number(engine.engineID()));
2210 cache.addModifier("cmEngineDescription", engine.description());
2211 cache.addModifier("useEmbeddedProfile", QString::number(static_cast<int>(cmSettings.useEmbeddedProfile())));
2212 cache.addModifier("softProofingAllowed", QString::number(static_cast<int>(cmSettings.softProofingAllowed())));
2213 cache.addModifier("requestType", QString::number(static_cast<int>(requestType)));
2214 cache.addModifier("gsRes", QString::number(gsRes));
2215 cache.addModifier("useColorManagement", QString::number(static_cast<int>(cmSettings.useColorManagement())));
2216 cache.addModifier("doSoftProofing", QString::number(static_cast<int>(cmSettings.doSoftProofing())));
2217 cache.addModifier("doGamutCheck", QString::number(static_cast<int>(cmSettings.doGamutCheck())));
2218 cache.addModifier("useBlackPoint", QString::number(static_cast<int>(cmSettings.useBlackPoint())));
2219 cache.addModifier("imageRenderingIntent", QString::number(static_cast<int>(cmSettings.imageRenderingIntent())));
2220 addProfileToCacheModifiers(cache, "monitor", cmSettings.monitorProfile());
2221 addProfileToCacheModifiers(cache, "printer", cmSettings.printerProfile());
2222
2223 fromCache = imgInfo.lowResType != 0 && cache.canUseCachedImage() && cache.load(*this) && imgInfo.deserialize(cache);
2224
2225 if (fromCache)
2226 {
2227 cache.touch();
2228 return true;
2229 }
2230 }
2231 else
2232 fromCache = false;
2233
2234 return loadPicture(cache.getFilename(), page, cmSettings, requestType, gsRes, realCMYK, showMsg);
2235 }
2236
saveCache(ScImageCacheProxy & cache)2237 bool ScImage::saveCache(ScImageCacheProxy & cache)
2238 {
2239 return cache.enabled() && imgInfo.serialize(cache) && cache.save(*this);
2240 }
2241
loadPicture(const QString & fn,int page,const CMSettings & cmSettings,RequestType requestType,int gsRes,bool * realCMYK,bool showMsg)2242 bool ScImage::loadPicture(const QString & fn, int page, const CMSettings& cmSettings,
2243 RequestType requestType, int gsRes, bool *realCMYK, bool showMsg)
2244 {
2245 // requestType - 0: CMYK, 1: RGB, 3 : RawData, 4: Thumbnail
2246 // gsRes - is the resolution that ghostscript will render at
2247 bool isCMYK = false;
2248 bool ret = false;
2249 // bool inputProfIsEmbedded = false;
2250 if (realCMYK != nullptr)
2251 *realCMYK = false;
2252 bool bilevel = false;
2253 // short resolutionunit = 0;
2254 RequestType reqType = requestType;
2255 int cmsFlags = 0;
2256 int cmsProofFlags = 0;
2257 QScopedPointer<ScImgDataLoader> pDataLoader;
2258 QFileInfo fi = QFileInfo(fn);
2259 if (!fi.exists())
2260 return ret;
2261 QString ext = fi.suffix().toLower();
2262 QString profileName;
2263 bool hasEmbeddedProfile = false;
2264 QList<QByteArray> fmtList = QImageReader::supportedImageFormats();
2265 QStringList fmtImg;
2266 ScColorTransform xform;
2267 ScColorProfile inputProf;
2268 for (int i = 0; i < fmtList.count(); i++)
2269 {
2270 fmtImg.append( QString(fmtList[i].toLower()) );
2271 }
2272 // bool found = false;
2273
2274 // Do some basic checks when requestType is OutputProfile
2275 if (requestType == OutputProfile)
2276 {
2277 if (!cmSettings.useColorManagement())
2278 return false;
2279 ScColorProfile prof = cmSettings.outputProfile();
2280 if (prof.isNull())
2281 return false;
2282 eColorSpaceType cspace = prof.colorSpace();
2283 if (cspace != ColorSpace_Rgb && cspace != ColorSpace_Cmyk)
2284 return false;
2285 }
2286 QString ext2 = getImageType(fn);
2287 if (ext.isEmpty() || (!ext2.isEmpty() && (ext2 != ext)))
2288 ext = ext2;
2289 if (extensionIndicatesPDF(ext))
2290 pDataLoader.reset( new ScImgDataLoader_PDF() );
2291 else if (extensionIndicatesEPSorPS(ext))
2292 pDataLoader.reset( new ScImgDataLoader_PS() );
2293 else if (extensionIndicatesJPEG(ext))
2294 pDataLoader.reset( new ScImgDataLoader_JPEG() );
2295 else if (extensionIndicatesPNG(ext))
2296 pDataLoader.reset( new ScImgDataLoader_PNG() );
2297 else if (extensionIndicatesPSD(ext))
2298 {
2299 pDataLoader.reset( new ScImgDataLoader_PSD() );
2300 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2301 }
2302 else if (extensionIndicatesTIFF(ext))
2303 pDataLoader.reset( new ScImgDataLoader_TIFF() );
2304 else if (ext == "pat")
2305 pDataLoader.reset( new ScImgDataLoader_GIMP() );
2306 else if (ext == "pgf")
2307 pDataLoader.reset( new ScImgDataLoader_PGF() );
2308 else if ((ext == "pct") || (ext == "pic") || (ext == "pict"))
2309 pDataLoader.reset( new ScImgDataLoader_PICT() );
2310 else if (ext == "wpg")
2311 pDataLoader.reset( new ScImgDataLoader_WPG() );
2312 else if (ext == "ora")
2313 {
2314 pDataLoader.reset( new ScImgDataLoader_ORA() );
2315 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2316 }
2317 else if (ext == "kra")
2318 {
2319 pDataLoader.reset( new ScImgDataLoader_KRA() );
2320 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2321 }
2322 #ifdef GMAGICK_FOUND
2323 else if (fmtImg.contains(ext))
2324 pDataLoader.reset( new ScImgDataLoader_QT() );
2325 else
2326 {
2327 pDataLoader.reset( new ScImgDataLoader_GMagick() );
2328 pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
2329 }
2330 #else
2331 else
2332 pDataLoader.reset( new ScImgDataLoader_QT() );
2333 #endif
2334
2335 if (pDataLoader->loadPicture(fn, page, gsRes, (requestType == Thumbnail)))
2336 {
2337 QImage::operator=(pDataLoader->image());
2338 imgInfo = pDataLoader->imageInfoRecord();
2339 if (requestType == Thumbnail)
2340 reqType = RGBData;
2341 // if (!cmSettings.useColorManagement() || !useProf)
2342 // {
2343 // imgInfo.isEmbedded = false;
2344 // imgInfo.profileName = "";
2345 // }
2346 if (imgInfo.colorspace == ColorSpaceCMYK)
2347 {
2348 isCMYK = true;
2349 if (realCMYK)
2350 *realCMYK = true;
2351 }
2352 else if (imgInfo.colorspace == ColorSpaceGray)
2353 bilevel = true;
2354 pDataLoader->image() = QImage();
2355 }
2356 else
2357 {
2358 if (ScCore->usingGUI() && pDataLoader->issuedErrorMsg() && showMsg)
2359 {
2360 ScMessageBox::critical(ScCore->primaryMainWindow(), CommonStrings::trWarning, pDataLoader->getMessage());
2361 }
2362 else if (pDataLoader->issuedErrorMsg())
2363 {
2364 QString msg = pDataLoader->getMessage();
2365 qWarning("%s", msg.toLocal8Bit().data() );
2366 }
2367 return false;
2368 }
2369
2370 if (!(extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())) //TODO: Unsure about this one!
2371 {
2372 if (isNull())
2373 return ret;
2374 }
2375
2376 QByteArray embeddedProfile = pDataLoader->embeddedProfile();
2377 if (cmSettings.useColorManagement())
2378 {
2379 if ((embeddedProfile.size() > 0 ) && (cmSettings.useEmbeddedProfile()))
2380 {
2381 inputProf = cmSettings.doc()->colorEngine.openProfileFromMem(embeddedProfile);
2382 // inputProfIsEmbedded = true;
2383 }
2384 else
2385 {
2386 QString profilePath;
2387 //CB If this is null, customfiledialog/picsearch/ScPreview might be sending it
2388 Q_ASSERT(cmSettings.doc()!=nullptr);
2389 if (isCMYK)
2390 {
2391 if (ScCore->InputProfilesCMYK.contains(cmSettings.profileName()) && (cmSettings.profileName() != cmSettings.doc()->cmsSettings().DefaultImageCMYKProfile))
2392 {
2393 imgInfo.profileName = cmSettings.profileName();
2394 // inputProfIsEmbedded = true;
2395 profilePath = ScCore->InputProfilesCMYK[imgInfo.profileName];
2396 inputProf = cmSettings.doc()->colorEngine.openProfileFromFile(profilePath);
2397 }
2398 else
2399 {
2400 inputProf = cmSettings.doc()->DocInputImageCMYKProf;
2401 imgInfo.profileName = cmSettings.doc()->cmsSettings().DefaultImageCMYKProfile;
2402 // inputProfIsEmbedded = false;
2403 }
2404 }
2405 else if (bilevel && (reqType == CMYKData))
2406 inputProf = nullptr; // Workaround to map directly gray to K channel
2407 else if (ScCore->InputProfiles.contains(cmSettings.profileName()) && (cmSettings.profileName() != cmSettings.doc()->cmsSettings().DefaultImageRGBProfile))
2408 {
2409 imgInfo.profileName = cmSettings.profileName();
2410 profilePath = ScCore->InputProfiles[imgInfo.profileName];
2411 // inputProfIsEmbedded = true;
2412 inputProf = cmSettings.doc()->colorEngine.openProfileFromFile(profilePath);
2413 }
2414 else
2415 {
2416 inputProf = cmSettings.doc()->DocInputImageRGBProf;
2417 imgInfo.profileName = cmSettings.doc()->cmsSettings().DefaultImageRGBProfile;
2418 // inputProfIsEmbedded = false;
2419 }
2420 }
2421 }
2422 else if ((cmSettings.useColorManagement() && embeddedProfile.size() > 0) && (cmSettings.useEmbeddedProfile()))
2423 {
2424 inputProf = cmSettings.doc()->colorEngine.openProfileFromMem(embeddedProfile);
2425 // inputProfIsEmbedded = true;
2426 }
2427 else if (cmSettings.colorManagementAllowed() && isCMYK)
2428 inputProf = ScCore->defaultCMYKProfile;
2429 else if (cmSettings.colorManagementAllowed() && bilevel && (reqType == CMYKData))
2430 inputProf = nullptr; // Workaround to map directly gray to K channel
2431 else if (cmSettings.colorManagementAllowed())
2432 inputProf = ScCore->defaultRGBProfile;
2433 ScColorProfile screenProf = cmSettings.monitorProfile() ? cmSettings.monitorProfile() : ScCore->defaultRGBProfile;
2434 ScColorProfile printerProf = cmSettings.printerProfile() ? cmSettings.printerProfile() : ScCore->defaultCMYKProfile;
2435 if (cmSettings.colorManagementAllowed() && inputProf && screenProf && printerProf)
2436 {
2437 ScColorMgmtEngine engine(cmSettings.doc() ? cmSettings.doc()->colorEngine : ScCore->defaultEngine);
2438 eColorFormat inputProfFormat = pDataLoader->pixelFormat();
2439 eColorFormat outputProfFormat = Format_YMCK_8;
2440 eColorSpaceType inputProfColorSpace = inputProf.colorSpace();
2441 eColorSpaceType outputProfColorSpace = printerProf.colorSpace();
2442 if (inputProfColorSpace == ColorSpace_Gray)
2443 inputProfFormat = Format_GRAY_8; // Grayscale is still a bit tricky
2444 if (outputProfColorSpace == ColorSpace_Rgb)
2445 outputProfFormat = Format_BGRA_8;
2446 else if (outputProfColorSpace == ColorSpace_Cmyk)
2447 outputProfFormat = Format_YMCK_8;
2448 ScColorSpace inputCSpace = engine.createColorSpace(inputProf, inputProfFormat);
2449 ScColorSpace screenCSpace = engine.createColorSpace(screenProf, Format_BGRA_8);
2450 ScColorSpace outputCSpace;
2451 if (cmSettings.useColorManagement() && cmSettings.doSoftProofing())
2452 {
2453 cmsProofFlags |= Ctf_Softproofing;
2454 if (cmSettings.doGamutCheck())
2455 {
2456 cmsProofFlags |= Ctf_GamutCheck;
2457 }
2458 }
2459 if (!cmSettings.useColorManagement() || cmSettings.useBlackPoint())
2460 cmsFlags |= Ctf_BlackPointCompensation;
2461 switch (reqType)
2462 {
2463 case CMYKData: // CMYK
2464 // if ((!isCMYK && (outputProfColorSpace == icSigCmykData)) || (isCMYK && (outputProfColorSpace == icSigRgbData)) )
2465 xform = inputCSpace.createTransform(printerProf, outputProfFormat, cmSettings.imageRenderingIntent(), cmsFlags);
2466 if (outputProfColorSpace != ColorSpace_Cmyk )
2467 *realCMYK = isCMYK = false;
2468 outputCSpace = engine.createColorSpace(printerProf, outputProfFormat);
2469 break;
2470 case Thumbnail:
2471 case RGBData: // RGB
2472 if (cmSettings.useColorManagement() && cmSettings.doSoftProofing())
2473 {
2474 if ((imgInfo.profileName == cmSettings.defaultImageRGBProfile()) || (imgInfo.profileName == cmSettings.defaultImageCMYKProfile()))
2475 {
2476 if (isCMYK && (inputProfFormat == Format_CMYK_8))
2477 xform = cmSettings.cmykImageProofingTransform();
2478 else if (inputProfFormat == Format_BGRA_8)
2479 xform = cmSettings.rgbImageProofingTransform();
2480 }
2481 if (!xform)
2482 {
2483 xform = inputCSpace.createProofingTransform(screenCSpace, printerProf,
2484 cmSettings.intent(), Intent_Relative_Colorimetric, cmsFlags | cmsProofFlags);
2485 }
2486 }
2487 else if (cmSettings.softProofingAllowed() || isCMYK)
2488 xform = inputCSpace.createTransform(screenCSpace, cmSettings.intent(), cmsFlags);
2489 else
2490 {
2491 if (pDataLoader->useRawImage())
2492 {
2493 QImage::operator=(pDataLoader->r_image.convertToQImage(false));
2494 profileName = imgInfo.profileName;
2495 hasEmbeddedProfile = imgInfo.isEmbedded;
2496 imgInfo = pDataLoader->imageInfoRecord();
2497 imgInfo.profileName = profileName;
2498 imgInfo.isEmbedded = hasEmbeddedProfile;
2499 // JG : this line overwrite image profile info and should not be needed here!!!!
2500 // imgInfo = pDataLoader->imageInfoRecord();
2501 }
2502 }
2503 outputProfColorSpace = ColorSpace_Rgb;
2504 outputCSpace = screenCSpace;
2505 break;
2506 case RawData: // no Conversion just raw Data
2507 xform = nullptr;
2508 if (pDataLoader->useRawImage())
2509 {
2510 QImage::operator=(pDataLoader->r_image.convertToQImage(true, true));
2511 profileName = imgInfo.profileName;
2512 hasEmbeddedProfile = imgInfo.isEmbedded;
2513 imgInfo = pDataLoader->imageInfoRecord();
2514 imgInfo.profileName = profileName;
2515 imgInfo.isEmbedded = hasEmbeddedProfile;
2516 // JG : this line overwrite image profile info and should not be needed here!!!!
2517 // imgInfo = pDataLoader->imageInfoRecord();
2518 }
2519 break;
2520 case OutputProfile: // CMYK
2521 ScColorProfile outputProfile = cmSettings.outputProfile();
2522 outputProfColorSpace = outputProfile.colorSpace();
2523 if ( outputProfColorSpace == ColorSpace_Rgb )
2524 outputProfFormat = Format_BGRA_8;
2525 else if ( outputProfColorSpace == ColorSpace_Cmyk )
2526 outputProfFormat = Format_YMCK_8;
2527 xform = inputCSpace.createTransform(outputProfile, outputProfFormat, cmSettings.imageRenderingIntent(), cmsFlags);
2528 isCMYK = (outputProfColorSpace == ColorSpace_Cmyk);
2529 if (realCMYK)
2530 *realCMYK = isCMYK;
2531 outputCSpace = engine.createColorSpace(outputProfile, outputProfFormat);
2532 break;
2533 }
2534 if (xform && outputCSpace)
2535 {
2536 if (pDataLoader->useRawImage())
2537 {
2538 QImage::operator=(QImage(pDataLoader->r_image.width(), pDataLoader->r_image.height(), QImage::Format_ARGB32));
2539 profileName = imgInfo.profileName;
2540 hasEmbeddedProfile = imgInfo.isEmbedded;
2541 imgInfo = pDataLoader->imageInfoRecord();
2542 imgInfo.profileName = profileName;
2543 imgInfo.isEmbedded = hasEmbeddedProfile;
2544 // JG : this line overwrite image profile info and should not be needed here!!!!
2545 // imgInfo = pDataLoader->imageInfoRecord();
2546 }
2547 uchar* ptr2 = nullptr;
2548 for (int i = 0; i < height(); i++)
2549 {
2550 uchar* ptr = scanLine(i);
2551 ptr2 = pDataLoader->useRawImage() ? pDataLoader->r_image.scanLine(i) : nullptr;
2552 if ((inputProfFormat == Format_GRAY_8) && (outputProfColorSpace != ColorSpace_Cmyk))
2553 {
2554 unsigned char* ucs = ptr2 ? (ptr2 + 1) : (ptr + 1);
2555 unsigned char* uc = new unsigned char[width()];
2556 for (int uci = 0; uci < width(); ++uci)
2557 {
2558 uc[uci] = *ucs;
2559 ucs += 4;
2560 }
2561 xform.apply(uc, ptr, width());
2562 delete[] uc;
2563 }
2564 else if ((inputProfFormat == Format_GRAY_8) && (outputProfColorSpace == ColorSpace_Cmyk))
2565 {
2566 unsigned char value;
2567 unsigned char* ucs = ptr2 ? ptr2 : ptr;
2568 unsigned char* uc = ptr;
2569 for (int uci = 0; uci < width(); ++uci, uc += 4)
2570 {
2571 value = 255 - *(ucs + 1);
2572 uc[0] = uc[1] = uc[2] = 0;
2573 uc[3] = value;
2574 ucs += 4;
2575 }
2576 }
2577 else
2578 {
2579 inputCSpace.convert(outputCSpace, (eRenderIntent) 0, 0, ptr2 ? ptr2 : ptr, ptr, width(), &xform);
2580 }
2581 if (pDataLoader->useRawImage())
2582 {
2583 // This might fix Bug #6328, please test.
2584 /*if (outputProfColorSpace != ColorSpace_Cmyk && bilevel)
2585 {
2586 QRgb alphaFF;
2587 QRgb *p;
2588 p = (QRgb *) ptr;
2589 for (int j = 0; j < width(); j++, p++)
2590 {
2591 alphaFF = qRgba(0,0,0,ptr2[3]);
2592 *p |= alphaFF;
2593 ptr2 += 4;
2594 }
2595 }*/
2596 // FIXME not valid if input or output colorspace are not 8bit / channels
2597 if (inputCSpace.hasAlphaChannel() && outputCSpace.hasAlphaChannel())
2598 {
2599 uint inputAlphaI = inputCSpace.alphaIndex();
2600 uint outputAlphaI = outputCSpace.alphaIndex();
2601 uint inputBytes = inputCSpace.bytesPerChannel() * inputCSpace.numChannels();
2602 uint outputBytes = outputCSpace.bytesPerChannel() * outputCSpace.numChannels();
2603 uchar* in = ptr2 + inputAlphaI * inputCSpace.bytesPerChannel();
2604 uchar* out = ptr + outputAlphaI * outputCSpace.bytesPerChannel();
2605 for (int j = 0; j < width(); ++j)
2606 {
2607 *out = *in;
2608 in += inputBytes;
2609 out += outputBytes;
2610 }
2611 }
2612 }
2613 }
2614 }
2615 }
2616 else
2617 {
2618 switch (reqType)
2619 {
2620 case CMYKData:
2621 if (!isCMYK)
2622 {
2623 if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
2624 {
2625 QImage::operator=(pDataLoader->r_image.convertToQImage(false));
2626 profileName = imgInfo.profileName;
2627 hasEmbeddedProfile = imgInfo.isEmbedded;
2628 imgInfo = pDataLoader->imageInfoRecord();
2629 imgInfo.profileName = profileName;
2630 imgInfo.isEmbedded = hasEmbeddedProfile;
2631 // JG : this line overwrite image profile info and should not be needed here!!!!
2632 // imgInfo = pDataLoader->imageInfoRecord();
2633 }
2634 unsigned char cc, cm, cy ,ck;
2635 QRgb *ptr;
2636 for (int i = 0; i < height(); i++)
2637 {
2638 ptr = (QRgb *) scanLine(i);
2639 for (int j = 0; j < width(); j++)
2640 {
2641 cc = 255 - qRed(*ptr);
2642 cm = 255 - qGreen(*ptr);
2643 cy = 255 - qBlue(*ptr);
2644 ck = qMin(qMin(cc, cm), cy);
2645 *ptr++ = qRgba(cc-ck,cm-ck,cy-ck,ck);
2646 }
2647 }
2648 }
2649 else
2650 {
2651 if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
2652 {
2653 QImage::operator=(pDataLoader->r_image.convertToQImage(true, true));
2654 profileName = imgInfo.profileName;
2655 hasEmbeddedProfile = imgInfo.isEmbedded;
2656 imgInfo = pDataLoader->imageInfoRecord();
2657 imgInfo.profileName = profileName;
2658 imgInfo.isEmbedded = hasEmbeddedProfile;
2659 // JG : this line overwrite image profile info and should not be needed here!!!!
2660 // imgInfo = pDataLoader->imageInfoRecord();
2661 }
2662 }
2663 break;
2664 case RGBData:
2665 case Thumbnail:
2666 if (isCMYK)
2667 {
2668 if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
2669 {
2670 QImage::operator=(pDataLoader->r_image.convertToQImage(true));
2671 profileName = imgInfo.profileName;
2672 hasEmbeddedProfile = imgInfo.isEmbedded;
2673 imgInfo = pDataLoader->imageInfoRecord();
2674 imgInfo.profileName = profileName;
2675 imgInfo.isEmbedded = hasEmbeddedProfile;
2676 // JG : this line overwrite image profile info and should not be needed here!!!!
2677 // imgInfo = pDataLoader->imageInfoRecord();
2678 }
2679 else
2680 {
2681 unsigned char cr, cg, cb, ck;
2682 QRgb *ptr;
2683 for (int i = 0; i < height(); i++)
2684 {
2685 ptr = (QRgb *) scanLine(i);
2686 for (int j = 0; j < width(); j++)
2687 {
2688 ck = qAlpha(*ptr);
2689 cr = 255 - qMin(255, qRed(*ptr) + ck);
2690 cg = 255 - qMin(255, qGreen(*ptr) + ck);
2691 cb = 255 - qMin(255, qBlue(*ptr) + ck);
2692 *ptr++ = qRgba(cr,cg,cb,255);
2693 }
2694 }
2695 }
2696 }
2697 else
2698 {
2699 if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
2700 {
2701 QImage::operator=(pDataLoader->r_image.convertToQImage(false));
2702 profileName = imgInfo.profileName;
2703 hasEmbeddedProfile = imgInfo.isEmbedded;
2704 imgInfo = pDataLoader->imageInfoRecord();
2705 imgInfo.profileName = profileName;
2706 imgInfo.isEmbedded = hasEmbeddedProfile;
2707 // JG : this line overwrite image profile info and should not be needed here!!!!
2708 // imgInfo = pDataLoader->imageInfoRecord();
2709 }
2710 }
2711 break;
2712 case RawData:
2713 if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
2714 {
2715 QImage::operator=(pDataLoader->r_image.convertToQImage(true, true));
2716 profileName = imgInfo.profileName;
2717 hasEmbeddedProfile = imgInfo.isEmbedded;
2718 imgInfo = pDataLoader->imageInfoRecord();
2719 imgInfo.profileName = profileName;
2720 imgInfo.isEmbedded = hasEmbeddedProfile;
2721 // JG : this line overwrite image profile info and should not be needed here!!!!
2722 // imgInfo = pDataLoader->imageInfoRecord();
2723 }
2724 break;
2725 default: // just to silence compiler
2726 break;
2727 }
2728 }
2729 // if (((reqType == CMYKData) || ((reqType == RawData) && isCMYK)) && !bilevel)
2730 // setAlphaBuffer(false);
2731 setDotsPerMeterX (qMax(2834, (int) (imgInfo.xres / 0.0254)));
2732 setDotsPerMeterY (qMax(2834, (int) (imgInfo.yres / 0.0254)));
2733 imgInfo.isEmbedded &= cmSettings.useEmbeddedProfile();
2734 if (ScCore->usingGUI() && pDataLoader->issuedWarningMsg() && showMsg)
2735 {
2736 ScMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning, pDataLoader->getMessage());
2737 }
2738 else if (pDataLoader->issuedWarningMsg())
2739 {
2740 QString msg = pDataLoader->getMessage();
2741 qWarning("%s", msg.toLocal8Bit().data() );
2742 }
2743 return true;
2744 }
2745
hasSmoothAlpha() const2746 bool ScImage::hasSmoothAlpha() const
2747 {
2748 int h = height();
2749 int w = width();
2750 QSet<int> alpha;
2751 const QRgb *s;
2752 for (int yi = 0; yi < h; ++yi)
2753 {
2754 s = (const QRgb*) constScanLine(yi);
2755 for (int xi = 0; xi < w; ++xi)
2756 {
2757 alpha.insert(qAlpha(*s));
2758 if (alpha.count() > 2)
2759 return true;
2760 s++;
2761 }
2762 }
2763 return (alpha.count() > 2);
2764 }
2765