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