Aurelon Open API 8.1.1
Loading...
Searching...
No Matches
AOI_SoftProofRenderer.cpp
1#include "./AOI_SoftProofRenderer.h"
2#include "../GDocument.h"
3#include "../GRenderer.h"
4#include "../GLayer.h"
5#include "../CImport.h"
6#include "../GProfile.h"
7#include <PNG Library/png.h>
8extern "C" {
9#include <JPEG Library/jpeglib.h>
10}
11#include "../f_MUL.h"
12#include "../GInstance.h"
13#include <ACPL/Utilities.h>
14#include <ACPL/FileStream.h>
15#include <ACPL/XML.h>
16#include "../GVector.h"
17#include "../ColorSpace.h"
18#include "../GPlaceHolder.h"
19#include "../GRectangle.h"
20#include "../FindSpotcolorInLibraries.h"
21
22using namespace aur;
23using namespace aur::PDF;
24using namespace aur::ACPL;
25
26class ThumbnailRenderer : public aur::PDF::Renderer
27{
28public:
29 ThumbnailRenderer();
30 virtual ~ThumbnailRenderer();
31 void SetProfile( aur::PDF::Document* doc, aur::PDF::ProfilePtr refProf, aur::PDF::Intent intent, aur::PDF::CMM* deviceCMMs );
32 void CopyBits( uint8_t* dest, const aur::PDF::LRectangle& updateBounds, int32_t rowBytes, bool bImageAlphaBlending = false );
33 bool Update( int32_t, int32_t );
34 const aur::PDF::CMM* GetDeviceCMM() const;
35 void SetVisiblePlates( uint64_t mask );
36
37private:
38 typedef struct RGBValues
39 {
40 uint8_t v[256][3];
41 } RGBValues;
42 aur::PDF::ProfilePtr mProofProfile;
43 aur::PDF::CMM* mDeviceCMM;
44 aur::PDF::Intent mProofIntent;
45 bool mProofProfileOpen;
46 std::vector<RGBValues> mChannelRGB;
47 double mChannelSolidity[MAX_CHANNELS];
48 std::vector<aur::ACPL::String> mExtraPlates;
49 uint64_t mVisiblePlates;
50 bool mTransparencyGrid;
51 enum CopyVariant
52 {
53 eNoMatch,
54 eDeviceN,
55 eICC
56 } mWhichCopy;
57
58 void CopyBitsDeviceN( uint8_t*, const aur::PDF::LRectangle&, int32_t, bool bImageAlphaBlending );
59 void CopyBitsICC( uint8_t*, const aur::PDF::LRectangle&, int32_t, bool bImageAlphaBlending );
60 void CopyBitsNoMatch( uint8_t*, const aur::PDF::LRectangle&, int32_t, bool bImageAlphaBlending );
61 void CopyBitsSingleChannel( uint8_t*, const aur::PDF::LRectangle&, int32_t, bool bImageAlphaBlending );
62
63 void AddDocumentColorants( aur::PDF::Document* doc );
64 void DeterminePlates( PDF::Document* doc );
65};
66
67inline const aur::PDF::CMM* ThumbnailRenderer::GetDeviceCMM() const
68{
69 return mDeviceCMM;
70}
71
72static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
73{
74 fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr));
75}
76
85static bool SaveAsPNG( const FileSpec& filePath, const uint8_t* buf, int32_t width, int32_t height )
86{
87 uint8_t * * buffer = new uint8_t*[height];
88 int32_t lineLen = 4 * width;
89 for ( int32_t nCount = 0; nCount < height; nCount++ )
90 {
91 buffer[nCount] = new unsigned char[lineLen];
92 memcpy( buffer[nCount], buf + (nCount * lineLen) , lineLen );
93 }
94
95 FILE *fp;
96 png_structp png_ptr;
97 png_infop info_ptr;
98
99#if ACPL_WIN
100 fp = _wfopen( filePath.GetPOSIXPath().w_str(), L"wb" );
101#else
102 fp = fopen( filePath.GetPOSIXPath().ToUTF8(), "wb" );
103#endif
104 if( fp == NULL)
105 return false;
106
107 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
108
109 info_ptr = png_create_info_struct(png_ptr);
110 png_ptr->io_ptr = (png_voidp)fp;
111
112 png_set_compression_level(png_ptr, 6);
113
114 png_set_IHDR(png_ptr, info_ptr, width, height,
115 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
116 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
117
118 double filegamma_ = 0.5;
119
120 png_set_gAMA(png_ptr, info_ptr, filegamma_);
121 png_set_gAMA(png_ptr, info_ptr, 0.5 );
122
123 time_t gmt;
124 png_time mod_time;
125 png_text text_ptr[5];
126 time(&gmt);
127 png_convert_from_time_t(&mod_time, gmt);
128 png_set_tIME(png_ptr, info_ptr, &mod_time);
129 text_ptr[0].key = "Title";
130 text_ptr[0].text = "Preview";
131 text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
132 text_ptr[1].key = "Author";
133 text_ptr[1].text = "Aurelon";
134 text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
135 text_ptr[2].key = "Description";
136 text_ptr[2].text = "";
137 text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
138 text_ptr[3].key = "Creation Time";
139#if defined(PNG_TIME_RFC1123_SUPPORTED)
140 text_ptr[3].text = png_convert_to_rfc1123(png_ptr, &mod_time);
141#else
142 static const char short_months[12][4] =
143 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
144
145 text_ptr[3].text = new char[128];
146 sprintf(text_ptr[3].text, "%d %s %d %02d:%02d:%02d +0000",
147 mod_time.day % 32, short_months[(mod_time.month - 1) % 12],
148 mod_time.year, mod_time.hour % 24, mod_time.minute % 60,
149 mod_time.second % 61);
150#endif
151 text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE;
152 text_ptr[4].key = "Software";
153 text_ptr[4].text = "";
154 text_ptr[4].compression = PNG_TEXT_COMPRESSION_NONE;
155 png_set_text(png_ptr, info_ptr, text_ptr, 5);
156 png_ptr->write_data_fn = png_write_data;
157 png_write_info(png_ptr, info_ptr);
158
159 png_write_image(png_ptr, buffer);
160 png_write_end(png_ptr, info_ptr);
161 png_destroy_write_struct(&png_ptr, &info_ptr);
162 fclose(fp);
163
164 for (int i = 0; i < height; i++)
165 delete[] buffer[i];
166 delete[] buffer;
167
168 return true;
169}
170
180static bool SaveAsJPG(const ACPL::FileSpec& filePath, const uint8_t* buf, int32_t width, int32_t height, int32_t quality )
181{
182 /* This struct contains the JPEG compression parameters and pointers to
183 * working space (which is allocated as needed by the JPEG library).
184 * It is possible to have several such structures, representing multiple
185 * compression/decompression processes, in existence at once. We refer
186 * to any one struct (and its associated working data) as a "JPEG object".
187 */
188 struct jpeg_compress_struct cinfo;
189 /* This struct represents a JPEG error handler. It is declared separately
190 * because applications often want to supply a specialized error handler
191 * (see the second half of this file for an example). But here we just
192 * take the easy way out and use the standard error handler, which will
193 * print a message on stderr and call exit() if compression fails.
194 * Note that this struct must live as long as the main JPEG parameter
195 * struct, to avoid dangling-pointer problems.
196 */
197 struct jpeg_error_mgr jerr;
198 /* More stuff */
199 FILE * fp; /* target file */
200 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
201 int row_stride; /* physical row width in image buffer */
202
203 /* Step 1: allocate and initialize JPEG compression object */
204
205 /* We have to set up the error handler first, in case the initialization
206 * step fails. (Unlikely, but it could happen if you are out of memory.)
207 * This routine fills in the contents of struct jerr, and returns jerr's
208 * address which we place into the link field in cinfo.
209 */
210 cinfo.err = jpeg_std_error(&jerr);
211 /* Now we can initialize the JPEG compression object. */
212 jpeg_create_compress(&cinfo);
213
214 /* Step 2: specify data destination (eg, a file) */
215 /* Note: steps 2 and 3 can be done in either order. */
216
217 /* Here we use the library-supplied code to send compressed data to a
218 * stdio stream. You can also write your own code to do something else.
219 * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
220 * requires it in order to write binary files.
221 */
222#if ACPL_WIN
223 fp = _wfopen( filePath.GetPOSIXPath().w_str(), L"wb" );
224#else
225 fp = fopen( filePath.GetPOSIXPath().ToUTF8(), "wb" );
226#endif
227 if( fp == NULL)
228 return false;
229
230 jpeg_stdio_dest(&cinfo, fp);
231
232 /* Step 3: set parameters for compression */
233
234 /* First we supply a description of the input image.
235 * Four fields of the cinfo struct must be filled in:
236 */
237 cinfo.image_width = width; /* image width and height, in pixels */
238 cinfo.image_height = height;
239 cinfo.input_components = 3; /* # of color components per pixel */
240 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
241 /* Now use the library's routine to set default compression parameters.
242 * (You must set at least cinfo.in_color_space before calling this,
243 * since the defaults depend on the source color space.)
244 */
245 jpeg_set_defaults(&cinfo);
246 /* Now you can set any non-default parameters you wish to.
247 * Here we just illustrate the use of quality (quantization table) scaling:
248 */
249 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
250
251 /* Step 4: Start compressor */
252
253 /* TRUE ensures that we will write a complete interchange-JPEG file.
254 * Pass TRUE unless you are very sure of what you're doing.
255 */
256 jpeg_start_compress(&cinfo, TRUE);
257
258 /* Step 5: while (scan lines remain to be written) */
259 /* jpeg_write_scanlines(...); */
260
261 /* Here we use the library's state variable cinfo.next_scanline as the
262 * loop counter, so that we don't have to keep track ourselves.
263 * To keep things simple, we pass one scanline per call; you can pass
264 * more if you wish, though.
265 */
266 row_stride = width * 3; /* JSAMPLEs per row in image_buffer */
267
268 while (cinfo.next_scanline < cinfo.image_height) {
269 /* jpeg_write_scanlines expects an array of pointers to scanlines.
270 * Here the array is only one element long, but you could pass
271 * more than one scanline at a time if that's more convenient.
272 */
273 row_pointer[0] = (JSAMPROW)&buf[cinfo.next_scanline * row_stride];
274 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
275 }
276
277 /* Step 6: Finish compression */
278
279 jpeg_finish_compress(&cinfo);
280 /* After finish_compress, we can close the output file. */
281 fclose(fp);
282
283 /* Step 7: release JPEG compression object */
284
285 /* This is an important step since it will release a good deal of memory. */
286 jpeg_destroy_compress(&cinfo);
287
288 return true;
289}
290
304 mDPU( 0 ),
305 mProfile( NULL ),
306 mCMM( NULL )
307{
308 if( document )
309 {
310 mDocument = document->mDocument;
311 }
312}
313
323bool AOI_SoftProofRenderer::Setup( const FileSpec& referenceProfile, const FileSpec& outputProfile )
324{
325 if( !referenceProfile.Exists() )
326 return false;
327
328 if( !outputProfile.Exists() )
329 return false;
330
331 mCMM = new PDF::CMM();
332 mCMM->Open( outputProfile, PDF::CMM::eOutput, PDF::eRelativeColorimetric );
333
334 PDF::ColorSpaceObject* cs = NULL;
335
336 if( mDocument->GetPage()->GetPageColorSpace() )
337 cs = mDocument->GetPage()->GetPageColorSpace();
338 else if( mDocument->GetDocumentColorSpace() )
339 cs = mDocument->GetDocumentColorSpace();
340
341 if( cs && cs->ResourceType() == PDF::ICCColorSpace::eResourceType )
342 mProfile = new PDF::Profile( static_cast<PDF::ICCColorSpace*>( cs ) );
343 else
344 mProfile = new PDF::Profile( referenceProfile );
345
346 ThumbnailRenderer* renderer = new ThumbnailRenderer();
347 renderer->SetProfile( mDocument, mProfile, PDF::eRelativeColorimetric, mCMM );
348 mDocument->SetRenderer( renderer );
349
350 return true;
351}
352
357{
358 PDF::ColorSpaceObject* cs = NULL;
359 PDF::ProfilePtr profile = NULL;
360
361 if( mDocument->GetPage()->GetPageColorSpace() )
362 cs = mDocument->GetPage()->GetPageColorSpace();
363
364 if( cs && cs->ResourceType() == PDF::ICCColorSpace::eResourceType )
365 {
366 profile = new PDF::Profile( static_cast<PDF::ICCColorSpace*>( cs ) );
367 }
368
369 if( profile != NULL )
370 {
371 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->SetProfile( mDocument, profile, PDF::eRelativeColorimetric, mCMM );
372
373 profile->Dispose();
374 }
375 else
376 {
377 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->SetProfile( mDocument, mProfile, PDF::eRelativeColorimetric, mCMM );
378 }
379}
380
396bool AOI_SoftProofRenderer::Render( const FileSpec& outputPath, const char* thumbnailType, int32_t x, int32_t y, int32_t width, int32_t height, uint32_t quality, uint64_t visiblePlates )
397{
398 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->SetVisiblePlates( visiblePlates );
399
400 //Calculate DPU based on original page size
401 float srcPageWidth, srcPageHeight;
402 mDocument->GetPage()->GetDimensions( srcPageWidth, srcPageHeight );
403
404 if( srcPageWidth > srcPageHeight )
405 {
406 mDPU = width / srcPageWidth;
407 }
408 else
409 {
410 mDPU = height / srcPageHeight;
411 }
412
413 // Apply DPU scaling factor to renderer and render the document
414 PDF::Mapping map;
415 map.Reset();
416 map.map[0][0] = mDPU;
417 map.map[1][1] = mDPU;
418 map.map[2][0] = float( -1 * x * width );
419 map.map[2][1] = float( -1 * y * height );
420
421 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->SetMapping( map );
422
423 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->RenderDocument( mDocument, true, true, NULL );
424
425 // Calculate size of thumbnail in bytes and copy the data in buffer
426 int32_t destLineBytes;
427 if(::strcmp( thumbnailType, "png" ) == 0)
428 {
429 destLineBytes = width * (( (ThumbnailRenderer*)mDocument->GetRenderer() )->GetDeviceCMM()->GetSpaceComponents() + 1 );
430 }
431 else if(::strcmp( thumbnailType, "jpg" ) == 0)
432 {
433 destLineBytes = width * ( (ThumbnailRenderer*)mDocument->GetRenderer() )->GetDeviceCMM()->GetSpaceComponents();
434 }
435 else
436 {
437 return false;
438 }
439
440 uint32_t dataLen = destLineBytes * height;
441 uint8_t* data = new uint8_t[dataLen];
442 ::memset( data, 0, dataLen );
443 PDF::LRectangle ubounds;
444 ubounds.left = x;
445 ubounds.top = y;
446 ubounds.right = x + width;
447 ubounds.bottom = y + height;
448
449 if( ::strcmp( thumbnailType, "png" ) == 0 )
450 {
451 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->CopyBits( data, ubounds, destLineBytes );
452 SaveAsPNG( outputPath, data, width, height );
453 }
454 else if( ::strcmp( thumbnailType, "jpg" ) == 0 )
455 {
456 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->CopyBits( data, ubounds, destLineBytes, true );
457 SaveAsJPG( outputPath, data, width, height, quality );
458 }
459
460 delete[] data;
461 return true;
462}
463
464
472void AOI_SoftProofRenderer::UpdateRenderer( uint32_t width, uint32_t height )
473{
474 if( ( (ThumbnailRenderer*)mDocument->GetRenderer() )->Update( width, height ) )
475 {
476 mDocument->Invalidate();
477 }
478}
479
488{
489 return mDocument->GetRenderer()->mChannelNames[index];
490}
491
498{
499 return (uint32_t)mDocument->GetRenderer()->mChannelNames.size();
500}
501
502typedef enum EProfileSelector
503{
504 eCMYK_Vector = 0,
505 eCMYK_Image = 0,
506 eRGB_Vector = 1,
507 eRGB_Image = 1,
508 eGray_Vector = 2,
509 eGray_Image = 2,
510 eCIELab_Vector = 3,
511 eCIELab_Image = 3
512} EProfileSelector;
513
514static bool IsDeviceSpace( const PDF::ColorSpaceObject* inSpace )
515{
516 if( inSpace->ResourceType() == PDF::IndexColorSpace::eResourceType )
517 return IsDeviceSpace( static_cast<const PDF::IndexColorSpace*>( inSpace )->GetBase() );
518 return dynamic_cast<const PDF::DeviceColorSpace*>( inSpace ) != NULL;
519}
520
521static int GetIndex( const char* name )
522{
523 static const char * const kChannelNames[] =
524 {
525 "Cyan", "Magenta", "Yellow", "Black"
526 };
527 for( int i = 0; i != 4; ++i )
528 if( !::stricmp( name, kChannelNames[ i ] ) )
529 return i;
530 return -1;
531}
532
533static bool IsSubsetOfCMYK( PDF::ColorSpaceObject* cs, uint32_t mappingArray[4] )
534{
535 if( cs->ResourceType() != PDF::DeviceN::eResourceType || cs->NrOfComponents() > 4 )
536 return false;
537
538 PDF::DeviceN* devN = (PDF::DeviceN*)cs;
539 int index;
540 for( uint32_t i = 0 ; i != devN->NrOfComponents(); ++i )
541 {
542 if( ( index = GetIndex( devN->ChannelName( i ) ) ) == -1 )
543 return false;
544 mappingArray[ i ] = uint32_t( index );
545 }
546 return true;
547}
548
549static PDF::Intent ConvertIntent( bool vector, PDF::Intent intents[4], PDF::ColorSpaceObject* inSpace, bool wasDeviceSpace )
550{
551 PDF::Intent changedIntent = PDF::eMaxEnumIntent;
552 if( wasDeviceSpace )
553 switch( NONINDEXED( inSpace->Space() ) )
554 {
555 case PDF::RGBSpace :
556 changedIntent = intents[ vector ? eRGB_Vector : eRGB_Image ];
557 break;
558 case PDF::CMYKSpace :
559 changedIntent = intents[ vector ? eCMYK_Vector : eCMYK_Image ];
560 break;
561 case PDF::GraySpace :
562 changedIntent = intents[ vector ? eGray_Vector : eGray_Image ];
563 break;
564 case PDF::LABSpace :
565 changedIntent = intents[ vector ? eCIELab_Vector : eCIELab_Image ];
566 break;
567 default:
568 ;
569 }
570 return changedIntent;
571}
572
573static PDF::ColorSpace ConvertColorSpace( PDF::Document* doc, PDF::ColorSpaceObject*& inSpace, PDF::ColorSpaceObject* inRGB, PDF::ColorSpaceObject* inCMYK, PDF::ColorSpaceObject* inGray )
574{
575 PDF::ColorSpace changedSpace = PDF::NoSpace;
576 if( false )//job->mUseEmbeddedProfiles == eUseEmbeddedNone && inSpace->ResourceType() == PDF::ICCColorSpace::eResourceType )
577 {
578 switch( inSpace->Space() )
579 {
580 case PDF::RGBSpace :
581 inSpace = doc->GetDeviceRGBColorSpace();
582 changedSpace = PDF::RGBSpace;
583 break;
584 case PDF::CMYKSpace :
585 inSpace = doc->GetDeviceCMYKColorSpace();
586 changedSpace = PDF::CMYKSpace;
587 break;
588 case PDF::GraySpace :
589 inSpace = doc->GetDeviceGrayColorSpace();
590 changedSpace = PDF::GraySpace;
591 break;
592 default:
593 ;
594 }
595 }
596 switch( inSpace->ResourceType() )
597 {
598 case PDF::DeviceRGB::eResourceType :
599 if( inRGB )
600 {
601 inSpace = inRGB;
602 changedSpace = PDF::RGBSpace;
603 }
604 break;
605 case PDF::DeviceCMYK::eResourceType :
606 case PDF::IllustratorCMYK::eResourceType :
607 if( inCMYK )
608 {
609 inSpace = inCMYK;
610 changedSpace = PDF::CMYKSpace;
611 }
612 break;
613 case PDF::DeviceGray::eResourceType :
614 if( inGray )
615 {
616 inSpace = inGray;
617 changedSpace = PDF::GraySpace;
618 }
619 break;
620 case PDF::IndexColorSpace::eResourceType :
621 {
622 PDF::IndexColorSpace* idxCS = static_cast<PDF::IndexColorSpace*>( inSpace );
623 PDF::ColorSpaceObject* baseCS = idxCS->GetBase();
624 if( baseCS->ResourceType() != PDF::DeviceN::eResourceType ) // Conversion of index table to DeviceCMYK not implemented yet
625 {
626 changedSpace = ConvertColorSpace( doc, baseCS, inRGB, inCMYK, inGray );
627 if( baseCS != idxCS->GetBase() )
628 idxCS->SetBase( baseCS );
629 }
630 }
631 break;
632 case PDF::DeviceN::eResourceType :
633 if( inCMYK )
634 {
635 uint32_t cmykLink[4];
636 if( IsSubsetOfCMYK( inSpace, cmykLink ) )
637 {
638 inSpace = inCMYK;
639 changedSpace = PDF::CMYKSpace;
640 }
641 }
642 break;
643 }
644 return changedSpace;
645}
646
647static bool ConvertRasterToDeviceCMYK( PDF::RasterPtr ras, PDF::DocumentPtr doc, PDF::RasterPtr& newRaster )
648{
649 uint32_t cmykLink[4];
650 if( !IsSubsetOfCMYK( ras->GetColorSpace(), cmykLink ) )
651 return false;
652 uint32_t comps = ras->GetColorSpace()->NrOfComponents();
653
654 PDF::BitmapPtr bitmapPtr = ras->GetBitmap();
655 PDF::BitmapT bitmap = *bitmapPtr->GetBitmap();
656 bitmap.space = doc->GetDeviceCMYKColorSpace();
657 //bitmap.pixelSize = 32;
658 bitmap.rowBytes = bitmap.width * 4;
659
660 PDF::Import import( doc );
661 import.OpenRaster( bitmap );
662
663 uint8_t* buffer = new uint8_t[ bitmap.rowBytes ];
664 ::memset( buffer, 0, bitmap.rowBytes );
665 const uint8_t* data;
666 bitmapPtr->Open( NULL );
667
668 for( int32_t y = 0; y != bitmap.height; ++y )
669 {
670 uint8_t* out = buffer;
671 for( int32_t x = 0; x != bitmap.width; ++x )
672 {
673 data = bitmapPtr->GetPixelPtr( x, y );
674 for( uint32_t i = 0; i != comps; ++i )
675 out[ cmykLink[i] ] = data[i];
676 out += 4;
677 }
678 import.SaveLine( buffer );
679 }
680
681 bitmapPtr->Close();
682 delete[] buffer;
683
684 newRaster = import.CloseRaster( true ); // true because it will be overwritten anywway
685
686 newRaster->Map( ras->mapping() );
687
688 newRaster->SetBlendMode( ras->GetBlendMode() );
689 newRaster->SetBlendValue( ras->GetBlendValue() );
690 newRaster->SetIntent( ras->GetIntent() );
691 newRaster->SetVisible( ras->Visible() );
692 newRaster->SetLock( ras->Locked() );
693
694 return true;
695}
696
697static void AssignDefaultProfiles( PDF::ObjectPtr obj, PDF::ICCColorSpace* cs[3], PDF::Intent intents[4], PDF::DocumentPtr doc )
698{
699 PDF::ColorSpaceObject* ioCS;
700 PDF::ColorSpace changedSpace;
701 PDF::Intent changedIntent;
702 PDF::Color c;
703 uint32_t i, cmykLink[4];
704
705 bool isDeviceSpace;
706
707 while( doc->TraverseNext( obj ) )
708 {
709 PDF::VectorPtr vec = dynamic_cast<PDF::VectorPtr>( obj );
710 if( vec )
711 {
712 if( vec->GetStrokeStyle() )
713 {
714 if( vec->GetStrokeStyle()->ResourceType() == PDF::Solid::eResourceType )
715 {
716 PDF::SolidPtr col = static_cast<PDF::SolidPtr>( vec->GetStrokeStyle() );
717 ioCS = col->GetSpace();
718 isDeviceSpace = IsDeviceSpace( ioCS );
719 if( ( changedSpace = ConvertColorSpace( doc, ioCS, cs[eRGB_Vector], cs[eCMYK_Vector], cs[eGray_Vector] ) ) != PDF::NoSpace )
720 {
721 col->GetColor( c );
722 if( changedSpace == PDF::CMYKSpace )
723 {
724 if( IsSubsetOfCMYK( c.space, cmykLink ) )
725 {
726 PDF::Color org = c;
727 ::memset( c.channel, 0, 4*sizeof(uint16_t) );
728 for( i = 0 ; i != c.space->NrOfComponents(); ++i )
729 c.channel[ cmykLink[i] ] = org.channel[i];
730 if( vec->GetBlendMode() == PDF::eBMOverprint1 && vec->GetBlendValue() == 1 )
731 vec->SetBlendMode( PDF::eBMMultiply );
732 }
733 else if( c.space->ResourceType() == PDF::IllustratorCMYK::eResourceType &&
734 vec->GetBlendMode() == PDF::eBMOverprint1 && vec->GetBlendValue() == 1 )
735 {
736 for( i = 0 ; i != 4; ++i )
737 if( c.channel[i] == 0 ) // Any channel is not set
738 {
739 vec->SetBlendMode( PDF::eBMMultiply );
740 break;
741 }
742 }
743 }
744 c.space = ioCS;
745 col = new PDF::Solid( doc, c );
746 vec->SetStrokeStyle( col );
747 col->Dispose();
748 col = static_cast<PDF::SolidPtr>( vec->GetStrokeStyle() ); // Retrieve col because the SetStrokeStyle may use a shared instance
749 }
750 if( ( changedIntent = ConvertIntent( true, intents, ioCS, isDeviceSpace && changedSpace != PDF::NoSpace ) ) != PDF::eMaxEnumIntent )
751 vec->SetIntent( changedIntent );
752 }
753 else
754 {
755 PDF::ShadingSpacePtr shad = dynamic_cast<PDF::ShadingSpacePtr>( vec->GetStrokeStyle() );
756 if( shad )
757 {
758 ioCS = shad->GetColorSpace();
759 isDeviceSpace = IsDeviceSpace( ioCS );
760 if( ( changedSpace = ConvertColorSpace( doc, ioCS, cs[eRGB_Vector], cs[eCMYK_Vector], cs[eGray_Vector] ) ) != PDF::NoSpace )
761 {
762 if( changedSpace == PDF::CMYKSpace && IsSubsetOfCMYK( shad->GetColorSpace(), cmykLink ) )
763 {
764 shad->ConvertToDeviceCMYK( cmykLink );
765 if( vec->GetBlendMode() == PDF::eBMOverprint1 && vec->GetBlendValue() == 1 )
766 vec->SetBlendMode( PDF::eBMMultiply );
767 }
768 shad->SetColorSpace( ioCS );
769 }
770 if( ( changedIntent = ConvertIntent( true, intents, ioCS, isDeviceSpace && changedSpace != PDF::NoSpace ) ) != PDF::eMaxEnumIntent )
771 vec->SetIntent( changedIntent );
772 }
773 }
774 }
775 if( vec->GetFillStyle() )
776 {
777 if( vec->GetFillStyle()->ResourceType() == PDF::Solid::eResourceType )
778 {
779 PDF::SolidPtr col = static_cast<PDF::SolidPtr>( vec->GetFillStyle() );
780 ioCS = col->GetSpace();
781 isDeviceSpace = IsDeviceSpace( ioCS );
782 if( ( changedSpace = ConvertColorSpace( doc, ioCS, cs[eRGB_Vector], cs[eCMYK_Vector], cs[eGray_Vector] ) ) != PDF::NoSpace )
783 {
784 col->GetColor( c );
785 if( changedSpace == PDF::CMYKSpace )
786 {
787 if( IsSubsetOfCMYK( c.space, cmykLink ) )
788 {
789 PDF::Color org = c;
790 ::memset( c.channel, 0, 4*sizeof(uint16_t) );
791 for( i = 0 ; i != c.space->NrOfComponents(); ++i )
792 c.channel[ cmykLink[i] ] = org.channel[i];
793 if( vec->GetBlendMode() == PDF::eBMOverprint1 && vec->GetBlendValue() == 1 )
794 vec->SetBlendMode( PDF::eBMMultiply );
795 }
796 else if( c.space->ResourceType() == PDF::IllustratorCMYK::eResourceType &&
797 vec->GetBlendMode() == PDF::eBMOverprint1 && vec->GetBlendValue() == 1 )
798 {
799 for( i = 0 ; i != 4; ++i )
800 if( c.channel[i] == 0 ) // Any channel is not set
801 {
802 vec->SetBlendMode( PDF::eBMMultiply );
803 break;
804 }
805 }
806 }
807 c.space = ioCS;
808 col = new PDF::Solid( doc, c );
809 vec->SetFillStyle( col );
810 col->Dispose();
811 col = static_cast<PDF::SolidPtr>( vec->GetFillStyle() ); // Retrieve col because the SetFillStyle may use a shared instance
812 }
813 if( ( changedIntent = ConvertIntent( true, intents, ioCS, isDeviceSpace && changedSpace != PDF::NoSpace ) ) != PDF::eMaxEnumIntent )
814 vec->SetIntent( changedIntent );
815 }
816 else
817 {
818 PDF::ShadingSpacePtr shad = dynamic_cast<PDF::ShadingSpacePtr>( vec->GetFillStyle() );
819 if( shad )
820 {
821 ioCS = shad->GetColorSpace();
822 isDeviceSpace = IsDeviceSpace( ioCS );
823 if( ( changedSpace = ConvertColorSpace( doc, ioCS, cs[eRGB_Vector], cs[eCMYK_Vector], cs[eGray_Vector] ) ) != PDF::NoSpace )
824 {
825 if( changedSpace == PDF::CMYKSpace && IsSubsetOfCMYK( shad->GetColorSpace(), cmykLink ) )
826 {
827 shad->ConvertToDeviceCMYK( cmykLink );
828 if( vec->GetBlendMode() == PDF::eBMOverprint1 && vec->GetBlendValue() == 1 )
829 vec->SetBlendMode( PDF::eBMMultiply );
830 }
831 shad->SetColorSpace( ioCS );
832 }
833 if( ( changedIntent = ConvertIntent( true, intents, ioCS, isDeviceSpace && changedSpace != PDF::NoSpace ) ) != PDF::eMaxEnumIntent )
834 vec->SetIntent( changedIntent );
835 }
836 else
837 {
838 if( auto pattern = dynamic_cast<PDF::PatternSpacePtr>( vec->GetFillStyle() ) )
839 if( pattern->GetTile() && pattern->GetTile()->GetObject() )
840 AssignDefaultProfiles( pattern->GetTile()->GetObject(), cs, intents, doc );
841 }
842 }
843 }
844 }
845 else if( obj->GetType() == PDF::gRasterType )
846 {
847 PDF::RasterPtr raster = NULL;
848
849 raster = PDF::RasterPtr(obj);
850
851 ioCS = raster->GetColorSpace();
852 isDeviceSpace = IsDeviceSpace( ioCS );
853 if( ( changedSpace = ConvertColorSpace( doc, ioCS, cs[eRGB_Image], cs[eCMYK_Image], cs[eGray_Image] ) ) != PDF::NoSpace )
854 {
855 if( changedSpace == PDF::CMYKSpace && ConvertRasterToDeviceCMYK( raster, doc, raster ) )
856 {
857 if( obj->GetBlendMode() == PDF::eBMOverprint1 && obj->GetBlendValue() == 1 )
858 raster->SetBlendMode( PDF::eBMMultiply );
859 obj->Replace( raster );
860 delete obj;
861 obj = raster;
862 }
863 raster->SetColorSpace( ioCS );
864 ioCS = raster->GetColorSpace();
865 }
866 if( ( changedIntent = ConvertIntent( false, intents, ioCS, isDeviceSpace && changedSpace != PDF::NoSpace ) ) != PDF::eMaxEnumIntent )
867 raster->SetIntent( changedIntent );
868 }
869 else
870 {
871 PDF::SymbolPtr sym = dynamic_cast<PDF::SymbolPtr>( obj );
872 if( sym && sym->GetObject() )
873 AssignDefaultProfiles( sym->GetObject(), cs, intents, doc );
874 }
875 }
876}
877
888void AOI_SoftProofRenderer::AssignInputProfiles( const FileSpec& rgbProfile, const FileSpec& cmykProfile, const FileSpec& grayProfile, int32_t intent )
889{
890 PDF::ICCColorSpace* cs[3];
891 ACPL::FileSpec sp[3];
892 sp[1] = rgbProfile;
893 sp[0] = cmykProfile;
894 sp[2] = grayProfile;
895 for( int16_t i = 0; i < 3; i++ )
896 {
897 cs[i] = NULL;
898 if( sp[i].Exists() )
899 {
900 try
901 {
902 PDF::ICCColorSpace* newCS = new PDF::ICCColorSpace( mDocument, sp[i] );
903 cs[i] = (PDF::ICCColorSpace*)mDocument->FindEqualResource( newCS );
904 newCS->Dispose();
905 }
906 catch(...)
907 {
908 cs[i]->Dispose();
909 cs[i] = NULL;
910 }
911 }
912 }
913 PDF::Intent intents[4];
914 switch( intent )
915 {
916 case 0:
917 intents[0] = PDF::eRelativeColorimetric;
918 break;
919 case 1:
920 intents[0] = PDF::ePerceptual;
921 break;
922 case 2:
923 intents[0] = PDF::eAbsoluteColorimetric;
924 break;
925 default:
926 intents[0] = PDF::eRelativeColorimetric;
927 break;
928
929 }
930 intents[3] = intents[1] = intents[2] = intents[0];
931 AssignDefaultProfiles( NULL, cs, intents, mDocument );
932 for( int16_t i = 0; i < 3; i++ )
933 cs[i]->Dispose();
934}
935
948bool AOI_SoftProofRenderer::RenderTiles( const TileSettings& tileSettings, const FileSpec& outputPath, int16_t channelIndex, const UString& multichannelRender )
949{
950 TileGenerator tg( tileSettings );
951
952 float srcPageWidth, srcPageHeight;
953 mDocument->GetPage()->GetDimensions( srcPageWidth, srcPageHeight );
954 tg.InitPage( srcPageWidth, srcPageHeight );
955
956 if( srcPageWidth > srcPageHeight )
957 {
958 mDPU = tg.GetPageWidth() * 2 / srcPageWidth;
959 }
960 else
961 {
962 mDPU = tg.GetPageHeight() * 2 / srcPageHeight;
963 }
964 ACPL::FileStream* multiStream = NULL;
965
966 if( !multichannelRender.IsEmpty() )
967 {
968 ACPL::FileSpec multiSpec;
969 multiSpec.Make( multichannelRender );
970 if( !multiSpec.Exists() )
971 multiSpec.Create();
972 multiStream = new ACPL::FileStream( multiSpec );
973 }
974
975
976 int32_t multiPixelBytes, multiLineBytes;
977 bool hasAlpha;
978
979 if( channelIndex == -1 ) //render
980 {
981 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->SetVisiblePlates( tileSettings.mVisiblePlates );
982 }
983 else if( multiStream )
984 {
985 //read data sizes
986 *multiStream >> multiPixelBytes;
987 *multiStream >> multiLineBytes;
988 *multiStream >> hasAlpha;
989 }
990
991 //prepare full size preview jpeg
992 struct jpeg_compress_struct cinfo;
993 struct jpeg_error_mgr jerr;
994 /* More stuff */
995 FILE * fp; /* target file */
996 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
997 int row_stride; /* physical row width in image buffer */
998 cinfo.err = jpeg_std_error(&jerr);
999 jpeg_create_compress(&cinfo);
1000
1001 ACPL::UString filePath;
1002 {
1003 if( !outputPath.DirectoryExists() )
1004 outputPath.CreateDirectory();
1005 ACPL::FileSpec f2;
1006 f2.Make( outputPath, L"Converted.jpg" );
1007 filePath = f2.GetPOSIXPath();
1008 }
1009#if ACPL_WIN
1010 fp = _wfopen( filePath.w_str(), L"wb" );
1011#else
1012 fp = fopen( filePath.ToUTF8(), "wb" );
1013#endif
1014 if( fp == NULL)
1015 return false;
1016 jpeg_stdio_dest(&cinfo, fp);
1017 uint32_t jpegY = 0;
1018
1019 ACPL::FileSpec inputSpec, outputSpec;
1020
1021 int32_t currentZoomLevel = tg.GetNrZoomLevels() - 1;
1022 int32_t remainingNrOfTiles = tg.GetTotalNrOfTiles();
1023 uint32_t positionInZoomLevel;
1024 while( currentZoomLevel >= 0 )
1025 {
1026 tg.SetZoomLevel( currentZoomLevel );
1027
1028 positionInZoomLevel = 1;
1029
1030 uint32_t tilesVertical = 0;
1031
1032 outputSpec.CreateTemporary();
1033 { //streams control
1034 ACPL::FileStream outputStream( outputSpec );
1035 ACPL::FileStream* inputFileStream = NULL;
1036
1037 if( currentZoomLevel != tg.GetNrZoomLevels() - 1 )
1038 {
1039 inputFileStream = new ACPL::FileStream( inputSpec );
1040 }
1041 else
1042 {
1043 //end prepare jpeg
1044 cinfo.image_width = tg.GetScaledWidth(); /* image width and height, in pixels */
1045 cinfo.image_height = tg.GetScaledHeight();
1046 cinfo.input_components = 3; /* # of color components per pixel */
1047 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
1048 jpeg_set_defaults(&cinfo);
1049 jpeg_set_quality(&cinfo, tileSettings.mJpgQuality, TRUE /* limit to baseline-JPEG values */);
1050 jpeg_start_compress(&cinfo, TRUE);
1051 row_stride = tg.GetScaledWidth() * 3;
1052 }
1053
1054 for( uint32_t band = 0; band != tg.GetNrBands(); ++band )
1055 {
1056 uint32_t bandSize = tg.GetBandHeight() * tg.GetScaledWidth() * 3;
1057 uint8_t* bandData = new uint8_t[ bandSize ];
1058
1059 if( currentZoomLevel == tg.GetNrZoomLevels() - 1 )
1060 {
1061 uint8_t* doubleBandData = NULL;
1062 if( channelIndex == -1 )
1063 {
1064 doubleBandData = new uint8_t[ 4 * tg.GetBandHeight() * tg.GetScaledWidth() * 4 ];
1065
1066 RenderBand( doubleBandData, band * tg.GetBandHeight() * 2, 2 * tg.GetScaledHeight(), tg.GetScaledWidth() * 2, tg.GetBandHeight() * 2 );
1067 if( multiStream )
1068 SaveMultichannelRender( *multiStream, tg.GetScaledWidth() * 2, 2 * tg.GetBandHeight() );
1069 DownsampleRGBBy2( doubleBandData, bandData, 4 * 2 * tg.GetScaledWidth(), 2 * tg.GetBandHeight(), 2 * tg.GetScaledWidth(), 3 * tg.GetScaledWidth(), 4 );
1070 }
1071 else
1072 {
1073 doubleBandData = new uint8_t[ 4 * tg.GetBandHeight() * tg.GetScaledWidth() * 3 ];
1074 if( multiStream )
1075 LoadMultiChannelRender(doubleBandData, channelIndex, *multiStream, tg.GetScaledWidth() * 2, 2 * tg.GetBandHeight(), multiPixelBytes, multiLineBytes, hasAlpha );
1076 DownsampleRGBBy2( doubleBandData, bandData, 3 * 2 * tg.GetScaledWidth(), 2 * tg.GetBandHeight(), 2 * tg.GetScaledWidth(), 3 * tg.GetScaledWidth(), 3 );
1077 }
1078
1079
1080 uint32_t previewY = 0;
1081 while( previewY < tg.GetBandHeight() && jpegY < tg.GetScaledHeight() )
1082 {
1083 row_pointer[0] = (JSAMPROW)&bandData[previewY * row_stride];
1084 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1085 previewY++;
1086 jpegY++;
1087 }
1088 delete[] doubleBandData;
1089 }
1090 else
1091 {
1092 LoadBand( bandData, *inputFileStream, bandSize );
1093 }
1094
1095 for( uint32_t j = 0; j < tg.GetTilesVerticalInBand() && tilesVertical != tg.GetTilesVertical(); j++ )
1096 {
1097 for( uint32_t i = 0; i < tg.GetTilesHorizontal(); i++ )
1098 {
1099 tg.WriteTile( i, tilesVertical, remainingNrOfTiles, j, bandData, 3 * tg.GetScaledWidth(), outputPath, positionInZoomLevel );
1100 positionInZoomLevel++;
1101 }
1102 tilesVertical++;
1103 }
1104 if( currentZoomLevel > 0 )
1105 {
1106 uint32_t newBandSize = ( tg.GetBandHeight() / 2 ) * ( tg.GetScaledWidth() / 2 ) * 3;
1107 uint8_t* newBand = new uint8_t[ newBandSize ];
1108
1109 DownsampleRGBBy2( bandData, newBand, 3 * tg.GetScaledWidth(), tg.GetBandHeight(), tg.GetScaledWidth(), 3 * ( tg.GetScaledWidth() / 2 ), 3 );
1110 SaveBand( newBand, outputStream, newBandSize );
1111
1112 delete[] newBand;
1113 }
1114 delete[] bandData;
1115 }
1116 delete inputFileStream;
1117 }
1118 inputSpec.Delete();
1119 inputSpec = outputSpec;
1120 if( currentZoomLevel == tg.GetNrZoomLevels() - 1 )
1121 {
1122 jpeg_finish_compress(&cinfo);
1123 /* After finish_compress, we can close the output file. */
1124 jpeg_destroy_compress(&cinfo);
1125 fflush( fp );
1126 fclose( fp );
1127 ACPL_SLEEP( 1000 );
1128 }
1129 remainingNrOfTiles -= tg.GetTilesHorizontal() * tg.GetTilesVertical();
1130 currentZoomLevel--;
1131 }
1132 if( outputSpec.Exists() )
1133 outputSpec.Delete();
1134
1135 tg.CreateTilesXML( outputPath );
1136
1137 delete multiStream;
1138
1139 return true;
1140}
1141
1142void AOI_SoftProofRenderer::DownsampleRGBBy2( uint8_t* inBandData, uint8_t* outBandData, uint32_t inRowbytes, uint32_t inHeight, uint32_t inWidth, uint32_t outRowBytes, uint32_t inComponents )
1143{
1144 uint8_t* dest8 = outBandData;
1145 uint8_t* line;
1146 bool evenLine = false;
1147 bool merge = true;
1148 uint32_t height = inHeight / 2;
1149 uint32_t width = inWidth / 2;
1150 uint32_t y = 0;
1151 uint32_t x = 0;
1152
1153 uint16_t* extLine = new uint16_t[ width * inComponents ];
1154 uint16_t* dest16;
1155
1156 while( y != height )
1157 {
1158 line = inBandData;
1159 dest16 = extLine;
1160 if( !evenLine )
1161 memset( extLine, 0, 2 * width * inComponents );
1162 else
1163 y++;
1164 x = 0;
1165 merge = true;
1166 while( x != width )
1167 {
1168 *dest16++ += *line++;
1169 *dest16++ += *line++;
1170 *dest16++ += *line++;
1171 line += inComponents - 3;
1172
1173 if( merge )
1174 dest16 -= 3;
1175 else
1176 x++;
1177 merge = !merge;
1178 }
1179 inBandData += inRowbytes;
1180
1181 if( evenLine ) //dump 8bit
1182 {
1183 x = 0;
1184 dest16 = extLine;
1185 while( x != width )
1186 {
1187 *dest8++ = (uint8_t)( *dest16++ >> 2 );
1188 *dest8++ = (uint8_t)( *dest16++ >> 2 );
1189 *dest8++ = (uint8_t)( *dest16++ >> 2 );
1190 x++;
1191 }
1192 }
1193 evenLine = !evenLine;
1194 }
1195 delete[] extLine;
1196}
1197
1202{
1203 mProfile->Dispose();
1204 mCMM->Dispose();
1205 PDF::Renderer* renderer = mDocument->GetRenderer();
1206 delete renderer;
1207}
1208
1209void AOI_SoftProofRenderer::RenderBand( uint8_t* doubleBandData, uint32_t y, uint32_t height, uint32_t renderingWidth, uint32_t renderingHeight )
1210{
1211 UpdateRenderer( renderingWidth, renderingHeight );
1212 // Apply DPU scaling factor to renderer and render the document
1213 PDF::Mapping map;
1214 map.Reset();
1215 map.map[0][0] = mDPU;
1216 map.map[1][1] = mDPU;
1217 map.map[2][1] = - float( y );
1218
1219 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->SetMapping( map );
1220 mDocument->Invalidate();
1221 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->RenderDocument( mDocument, true, true, NULL );
1222
1223 PDF::LRectangle ubounds;
1224 ubounds.left = 0;
1225 ubounds.top = 0;
1226 ubounds.right = renderingWidth;
1227 ubounds.bottom = renderingHeight;
1228
1229 ( (ThumbnailRenderer*)mDocument->GetRenderer() )->CopyBits( doubleBandData, ubounds, renderingWidth * 4 );
1230}
1231
1232void AOI_SoftProofRenderer::SaveMultichannelRender( aur::ACPL::FileStream& stream, uint32_t renderingWidth, uint32_t renderingHeight )
1233{
1234 if( stream.GetMarker() == 0 )
1235 {
1236 //initial setup
1237 stream << mDocument->GetRenderer()->mDestPixelBytes;
1238 stream << mDocument->GetRenderer()->mDestLineBytes;
1239 stream << mDocument->GetRenderer()->mHasAlpha;
1240 }
1241 int32_t size = mDocument->GetRenderer()->mDestLineBytes * renderingHeight;
1242 stream.PutBytes( mDocument->GetRenderer()->mDestImage, size );
1243}
1244
1245void AOI_SoftProofRenderer::LoadMultiChannelRender( uint8_t* doubleBandData, int16_t channelIndex, aur::ACPL::FileStream& stream, uint32_t renderingWidth, uint32_t renderingHeight, uint32_t pixelBytes, uint32_t lineBytes, bool hasAlpha )
1246{
1247 int32_t size;
1248 uint8_t* linePointer = new uint8_t[ lineBytes ];
1249 uint8_t* p;
1250 uint16_t alpha;
1251 register uint8_t backDrop8;
1252
1253 for( uint32_t y = 0; y != renderingHeight; ++y )
1254 {
1255 size = lineBytes;
1256 stream.GetBytes( linePointer, size );
1257 p = linePointer;
1258 for (uint32_t x = 0; x != renderingWidth; ++x)
1259 {
1260 if( ( alpha = hasAlpha ? p[pixelBytes - 1] : 255 ) == 255 )
1261 backDrop8 = 255 - p[channelIndex];
1262 else if( alpha == 0 )
1263 {
1264 backDrop8 = 255;
1265 }
1266 else
1267 {
1268 register uint16_t backDrop;
1269 backDrop = ( 255 - alpha ) << 8;
1270 backDrop8 = uint8_t( ( backDrop + ( 255 - p[channelIndex] ) * alpha ) >> 8 );
1271 }
1272 doubleBandData[0] = doubleBandData[1] = doubleBandData[2] = backDrop8;
1273 doubleBandData += 3;
1274 p += pixelBytes;
1275 }
1276 }
1277 delete[] linePointer;
1278}
1279
1280void AOI_SoftProofRenderer::SaveBand( const uint8_t* bandData, ACPL::FileStream& fileStream, int32_t byteCount )
1281{
1282 fileStream.PutBytes( bandData, byteCount );
1283}
1284
1285void AOI_SoftProofRenderer::LoadBand( uint8_t* bandData, ACPL::FileStream& fileStream, int32_t byteCount )
1286{
1287 fileStream.GetBytes( bandData, byteCount );
1288}
1289
1290AOI_SoftProofRenderer::TileGenerator::TileGenerator( const TileSettings& settings ) :
1291mSettings( settings )
1292{
1293}
1294
1295void AOI_SoftProofRenderer::TileGenerator::InitPage( float pageWidth, float pageHeight )
1296{
1297 mPageWidth = (uint32_t)( pageWidth / 18 * mSettings.mMaxDpi );
1298 mPageHeight = (uint32_t)( pageHeight / 18 * mSettings.mMaxDpi );
1299
1300 uint32_t maxSize = mPageHeight;
1301
1302 if (mPageWidth > mPageHeight)
1303 maxSize = mPageWidth;
1304
1305 mNrOfzoomLevels = 1;
1306 while (maxSize > mSettings.mTileSize)
1307 {
1308 mNrOfzoomLevels++;
1309 maxSize /= 2;
1310 }
1311
1312 int32_t currentZoomlevel = mNrOfzoomLevels - 1;
1313 mTotalNrOfTiles = 1;
1314 uint32_t tilesperColumn, tilesPerRows;
1315 while( currentZoomlevel )
1316 {
1317 tilesPerRows = ( ( mPageWidth >> ( mNrOfzoomLevels - currentZoomlevel - 1 ) ) + mSettings.mTileSize - 1 ) / mSettings.mTileSize;
1318 tilesperColumn = ( ( mPageHeight >> ( mNrOfzoomLevels - currentZoomlevel - 1 ) ) + mSettings.mTileSize - 1 ) / mSettings.mTileSize;
1319 mTotalNrOfTiles += tilesPerRows * tilesperColumn;
1320 currentZoomlevel--;
1321 }
1322}
1323
1324void AOI_SoftProofRenderer::TileGenerator::SetZoomLevel( uint32_t level )
1325{
1326 mCurrentZoomLevel = level;
1327 mScaledHeight = mPageHeight >> ( mNrOfzoomLevels - level - 1 );
1328 mScaledWidth = mPageWidth >> ( mNrOfzoomLevels - level - 1 );
1329 mTilesHorizontal = ( mScaledWidth + mSettings.mTileSize - 1 ) / mSettings.mTileSize;
1330 mTilesVertical = ( mScaledHeight + mSettings.mTileSize - 1 ) / mSettings.mTileSize;
1331 uint32_t maxMem = mSettings.mMaxMemoryUsage * 1024 * 1024;
1332 uint32_t maxLines = maxMem / ( 3 * mScaledWidth );
1333 if( maxLines >= mScaledHeight )
1334 {
1335 mNrBands = 1;
1336 mBandHeight = mScaledHeight;
1337 }
1338 else
1339 {
1340 mBandHeight = maxLines / mSettings.mTileSize;
1341 mBandHeight = mBandHeight * mSettings.mTileSize;
1342 if( mBandHeight < mSettings.mTileSize )
1343 mBandHeight = mSettings.mTileSize;
1344 mNrBands = ( mScaledHeight + mBandHeight - 1 ) / mBandHeight;
1345 }
1346}
1347
1348bool AOI_SoftProofRenderer::TileGenerator::WriteTile( uint32_t x, uint32_t y, uint32_t remainingNrOfTiles, uint32_t yInBand, uint8_t* bandData, uint32_t rowbytes, const FileSpec& outputPath, uint32_t positionInZoomLevel )
1349{
1350 // tile position
1351 uint32_t tileX = mSettings.mTileSize * x;
1352 uint32_t tileY = mSettings.mTileSize * yInBand;
1353
1354 if( tileX > mScaledWidth|| tileY > mScaledHeight )
1355 return false;
1356
1357 // tile width
1358 uint32_t tileWidth = mSettings.mTileSize;
1359 if ( tileWidth > mScaledWidth )
1360 {
1361 tileWidth = mScaledWidth;
1362 }
1363 else if( ( x + 1 ) * mSettings.mTileSize > mScaledWidth )
1364 {
1365 tileWidth = mScaledWidth - tileX;
1366 }
1367
1368 if(tileWidth <= 0)
1369 return false;
1370
1371 // tile height
1372 uint32_t tileHeight = mSettings.mTileSize;
1373 if( tileHeight > mScaledHeight )
1374 {
1375 tileHeight = mScaledHeight;
1376 }
1377 else if( ( y + 1 ) * mSettings.mTileSize > mScaledHeight )
1378 {
1379 tileHeight = mScaledHeight - mSettings.mTileSize * y;
1380 }
1381
1382 if( tileHeight <= 0 )
1383 return false;
1384
1385 uint32_t folderIndex = ( remainingNrOfTiles - ( mTilesVertical * mTilesHorizontal - positionInZoomLevel ) - 1 ) / mSettings.mTileSize;
1386
1387 FileSpec tileSpec;
1388
1389 ACPL::UString folderName;
1390 folderName.Format( L"TileGroup%d", folderIndex );
1391 ACPL::FileSpec outputFolder;
1392 outputFolder.Make( outputPath, folderName );
1393 if( !outputFolder.DirectoryExists() )
1394 outputFolder.CreateDirectory();
1395
1396 ACPL::UString tileName;
1397 tileName.Format( L"%d-%d-%d.jpg", mCurrentZoomLevel, x, y );
1398 tileSpec.Make( outputFolder, tileName );
1399
1400 struct jpeg_compress_struct cinfo;
1401 struct jpeg_error_mgr jerr;
1402 FILE * fp; /* target file */
1403 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1404 int row_stride; /* physical row width in image buffer */
1405 cinfo.err = jpeg_std_error(&jerr);
1406 jpeg_create_compress(&cinfo);
1407
1408#if ACPL_WIN
1409 fp = _wfopen( tileSpec.GetPOSIXPath().w_str(), L"wb" );
1410#else
1411 fp = fopen( tileSpec.GetPOSIXPath().ToUTF8(), "wb" );
1412#endif
1413 if( fp == NULL)
1414 return false;
1415
1416 jpeg_stdio_dest(&cinfo, fp);
1417
1418 /* Step 3: set parameters for compression */
1419
1420 /* First we supply a description of the input image.
1421 * Four fields of the cinfo struct must be filled in:
1422 */
1423 cinfo.image_width = tileWidth; /* image width and height, in pixels */
1424 cinfo.image_height = tileHeight;
1425 cinfo.input_components = 3; /* # of color components per pixel */
1426 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
1427 jpeg_set_defaults(&cinfo);
1428 jpeg_set_quality(&cinfo, mSettings.mJpgQuality, TRUE /* limit to baseline-JPEG values */);
1429 jpeg_start_compress(&cinfo, TRUE);
1430
1431 row_stride = tileWidth * 3; /* JSAMPLEs per row in image_buffer */
1432
1433 uint8_t* buffer = bandData + tileY * rowbytes + tileX * 3;
1434
1435 while (cinfo.next_scanline < cinfo.image_height) {
1436 /* jpeg_write_scanlines expects an array of pointers to scanlines.
1437 * Here the array is only one element long, but you could pass
1438 * more than one scanline at a time if that's more convenient.
1439 */
1440 row_pointer[0] = (JSAMPROW)buffer;
1441 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1442 buffer += rowbytes;
1443 }
1444 jpeg_finish_compress(&cinfo);
1445 fclose(fp);
1446 jpeg_destroy_compress(&cinfo);
1447
1448 return true;
1449}
1450
1451void AOI_SoftProofRenderer::TileGenerator::CreateTilesXML( const FileSpec& outputPath )
1452{
1453 ACPL::FileSpec imageProperties;
1454 imageProperties.Make( outputPath, L"ImageProperties.xml" );
1455
1456 ACPL::XML imagePropertiesXML( "IMAGE_PROPERTIES" );
1457 ACPL::XML::Node* xmlRoot = imagePropertiesXML.root;
1458 xmlRoot->AddAttribute( "WIDTH", mPageWidth );
1459 xmlRoot->AddAttribute( "HEIGHT", mPageHeight );
1460 xmlRoot->AddAttribute( "NUMTILES", mTotalNrOfTiles );
1461 xmlRoot->AddAttribute( "NUMIMAGES", 1 );
1462 xmlRoot->AddAttribute( "VERSION", 1.8 );
1463 xmlRoot->AddAttribute( "TILESIZE", mSettings.mTileSize );
1464 xmlRoot->AddAttribute( "THUMBNAIL_WIDTH", 128 );
1465 xmlRoot->AddAttribute( "THUMBNAIL_HEIGHT", 128 );
1466 xmlRoot->AddAttribute( "RESOLUTION", mSettings.mMaxDpi );
1467 imagePropertiesXML.Save( imageProperties );
1468}
1469
1470uint32_t AOI_SoftProofRenderer::TileGenerator::GetPageWidth() const
1471{
1472 return mPageWidth;
1473}
1474
1475uint32_t AOI_SoftProofRenderer::TileGenerator::GetPageHeight() const
1476{
1477 return mPageHeight;
1478}
1479
1480uint32_t AOI_SoftProofRenderer::TileGenerator::GetNrBands() const
1481{
1482 return mNrBands;
1483}
1484
1485uint32_t AOI_SoftProofRenderer::TileGenerator::GetBandHeight() const
1486{
1487 return mBandHeight;
1488}
1489
1490uint32_t AOI_SoftProofRenderer::TileGenerator::GetScaledHeight() const
1491{
1492 return mScaledHeight;
1493}
1494
1495uint32_t AOI_SoftProofRenderer::TileGenerator::GetScaledWidth() const
1496{
1497 return mScaledWidth;
1498}
1499
1500uint32_t AOI_SoftProofRenderer::TileGenerator::GetTilesHorizontal() const
1501{
1502 return mTilesHorizontal;
1503}
1504
1505uint32_t AOI_SoftProofRenderer::TileGenerator::GetTilesVertical() const
1506{
1507 return mTilesVertical;
1508}
1509
1510uint32_t AOI_SoftProofRenderer::TileGenerator::GetTilesVerticalInBand() const
1511{
1512 return ( mBandHeight + mSettings.mTileSize - 1 ) / mSettings.mTileSize;
1513}
1514
1515uint32_t AOI_SoftProofRenderer::TileGenerator::GetNrZoomLevels() const
1516{
1517 return mNrOfzoomLevels;
1518}
1519
1520uint32_t AOI_SoftProofRenderer::TileGenerator::GetCurrentZoomLevel() const
1521{
1522 return mCurrentZoomLevel;
1523}
1524
1525uint32_t AOI_SoftProofRenderer::TileGenerator::GetTotalNrOfTiles() const
1526{
1527 return mTotalNrOfTiles;
1528}
1529
1546AOI_SoftProofRenderer::TileSettings::TileSettings( uint32_t tileSize, uint32_t minZoomSize, uint32_t maxMemoryUsage, float maxDpi, int16_t jpgQuality, uint64_t visiblePlates )
1547{
1548 mTileSize = tileSize;
1549 mMinZoomSize = minZoomSize;
1550 mMaxMemoryUsage = maxMemoryUsage;
1551 mMaxDpi = maxDpi;
1552 mJpgQuality = jpgQuality;
1553 mVisiblePlates = visiblePlates;
1554}
1555
1562void AOI_SoftProofRenderer::TileSettings::SetVisiblePlates( uint64_t visiblePlates )
1563{
1564 mVisiblePlates = visiblePlates;
1565}
1566
1567
1568ThumbnailRenderer::ThumbnailRenderer() :
1569PDF::Renderer(),
1570 mProofProfile( NULL ),
1571 mDeviceCMM( NULL ),
1572 mProofIntent( PDF::eRelativeColorimetric ),
1573 mProofProfileOpen( false ),
1574 mWhichCopy( eNoMatch )
1575{
1576 SetAntiAliasGlyph( true );
1577 mDeviceWidth = 1;
1578 mDeviceHeight = 1;
1579 for( uint32_t i = 0; i != MAX_CHANNELS; ++i )
1580 mChannelSolidity[i] = 0;
1581}
1582
1583ThumbnailRenderer::~ThumbnailRenderer()
1584{
1585 if( mProofProfile )
1586 {
1587 if( mProofProfileOpen )
1588 mProofProfile->Close( PDF::CMM::eInput, mProofIntent );
1589 mProofProfile->Dispose();
1590 }
1591 mDeviceCMM->Dispose();
1592}
1593
1594void ThumbnailRenderer::SetProfile( PDF::Document* doc, PDF::ProfilePtr refProf, PDF::Intent intent, PDF::CMM* deviceCMM )
1595{
1596 refProf->Clone();
1597 if( mProofProfile )
1598 {
1599 if( mProofProfileOpen )
1600 mProofProfile->Close( PDF::CMM::eInput, mProofIntent );
1601 mProofProfile->Dispose();
1602 }
1603 mProofProfile = refProf;
1604 mProofIntent = intent;
1605 deviceCMM->Clone();
1606 mDeviceCMM->Dispose();
1607 mDeviceCMM = deviceCMM;
1608 mProofProfileOpen = false;
1609 mVisiblePlates = UINT64_MAX;
1610
1611 //
1612 // Clean up current setup
1613 //
1614 for( uint32_t i = 0; i != MAX_CHANNELS; ++i )
1615 mChannelSolidity[i] = 0;
1616 mChannelRGB.clear();
1617 mExtraPlates.clear();
1618 //
1619 // Add colorants from document
1620 //
1621 if( doc )
1622 {
1623 DeterminePlates( doc );
1624 AddDocumentColorants( doc );
1625 }
1626
1627 Setup( mProofProfile->GetFileSpec(), mExtraPlates, mDeviceWidth, mDeviceHeight, true, false, true );
1628
1629 switch( mProofProfile->GetSpace() )
1630 {
1631 case PDF::RGBSpace :
1632 if( mComponents > 3 )
1633 mWhichCopy = eDeviceN;
1634 else
1635 mWhichCopy = ::memcmp( &mProofProfile->GetHeader(), &mDeviceCMM->GetHeader(), 16 * sizeof(uint32_t) ) == 0 &&
1636 ::memcmp( &mProofProfile->GetHeader().illuminant, &mDeviceCMM->GetHeader().illuminant, 6 + 4 ) == 0 ? eNoMatch : eICC;
1637 break;
1638 case PDF::CMYKSpace :
1639 if( mComponents > 4 )
1640 mWhichCopy = eDeviceN;
1641 else
1642 mWhichCopy = eICC;
1643 break;
1644 default :
1645 mWhichCopy = eDeviceN;
1646 break;
1647 }
1648
1649 if( mWhichCopy == eICC || mWhichCopy == eDeviceN )
1650 {
1651 mProofProfile->Open( PDF::CMM::eInput, mProofIntent );
1652 /*if( mProofIntent == eRelativeColorimetric )
1653 {
1654 mProofProfile->GetCMM( PDF::CMM::eInput, mProofIntent )->BlackPointCompensation( 0.0F );
1655 }*/
1656 mProofProfileOpen = true;
1657 }
1658}
1659
1660bool ThumbnailRenderer::Update( int32_t inWidth, int32_t inHeight )
1661{
1662 if( mDeviceWidth != inWidth || mDeviceHeight != inHeight )
1663 {
1664 Setup( mProofProfile->GetFileSpec(), mExtraPlates, inWidth, inHeight, true, false, true );
1665 return true;
1666 }
1667 return false;
1668}
1669
1670static void DetermineCIELab( const double* in, PDF::ColorSpaceObject* inSpace, PDF::CMM* destCMM, double outLab[3] )
1671{
1672 // Simply copy the values if it is already CIELab
1673 if( inSpace->Space() == PDF::LABSpace )
1674 {
1675 outLab[0] = in[0];
1676 outLab[1] = in[1];
1677 outLab[2] = in[2];
1678 return;
1679 }
1680
1681 // Determine CIELab from other colorspaces
1682 uint32_t inComps = inSpace->NrOfComponents();
1683 uint32_t outComps = destCMM->GetSpaceComponents();
1684 if( inComps <= outComps && dynamic_cast<PDF::DeviceColorSpace*>( inSpace ) != NULL )
1685 {
1686 // Link channels
1687 uint32_t linkCnt = 0;
1688 double val[MAX_CHANNELS];
1689 ::memset( val, 0, MAX_CHANNELS*sizeof(double) );
1690 for( uint32_t i = 0; i != inComps && linkCnt == i; ++i )
1691 {
1692 const char* inChannelName = inSpace->ChannelName( i );
1693 for( uint32_t j = 0; j != outComps; ++j )
1694 {
1695 const char* outChannelName = destCMM->GetChannelName( j );
1696 if( ::stricmp( inChannelName, outChannelName ) == 0 )
1697 {
1698 val[ j ] = in[ i ];
1699 ++linkCnt;
1700 break;
1701 }
1702 }
1703 }
1704 // Convert to CIELab if there is a link
1705 if( linkCnt == inComps )
1706 {
1707 destCMM->Color2Lab( val, outLab );
1708 return;
1709 }
1710 }
1711 // If there is no link or not a device space
1712 uint16_t in16[MAX_CHANNELS];
1713 for( uint32_t i = 0; i != inComps; ++i )
1714 in16[i] = uint16_t( in[i] * PDF::Component1 );
1715 inSpace->GetCIELab( in16, outLab );
1716}
1717
1718void ThumbnailRenderer::DeterminePlates( PDF::Document* doc )
1719{
1720 //
1721 // Determine the plates based on the DeviceN colorspaces in the documents
1722 //
1723 uint32_t i,j;
1724 int32_t profileChannelCount = ColorSpaceComponents( mProofProfile->GetSpace() );
1725 std::vector<PDF::Document::Colorant> extraPlates;
1726 std::vector<PDF::Function*> colorantFunc;
1727 std::vector<PDF::ColorSpaceObject*> colorantSpace;
1728 std::vector<PDF::DeviceN*> devNs;
1729 PDF::Document::Colorant colorant;
1730
1731 PDF::DeviceN* devn = NULL;
1732 int32_t devNCnt = 0;
1733 while( ( devn = (PDF::DeviceN*)doc->TraverseCommitedResources( PDF::DeviceN::eResourceType, devn ) ) != NULL )
1734 {
1735 ++devNCnt;
1736 for( j = 0; j != devn->NrOfComponents(); ++j )
1737 {
1738 const char* n = devn->ChannelName( j );
1739 bool found = ::strcmp( n, "None" ) == 0 || ::strcmp( n, "All" ) == 0;
1740 // Find channel in process colors (profile)
1741 if( mProofProfile->GetSpace() == PDF::GraySpace || mProofProfile->GetSpace() == PDF::RGBSpace )
1742 found = true;
1743 else
1744 for( int32_t ch = 0; !found && ch != profileChannelCount; ++ch )
1745 {
1746 String profChannelName = mProofProfile->GetChannel( ch );
1747 found = profChannelName == n;
1748 }
1749 // Find channel in document colorants
1750 if( !found )
1751 found = doc->colorantIndex( n ) >= 0;
1752 if( !found )
1753 {
1754 for( i = 0; i != extraPlates.size() && ::stricmp( n, extraPlates[i].name ); ++i )
1755 ;
1756 if( i < uint32_t( doc->GetInstance()->GetMaxChannels()-profileChannelCount-doc->colorantCount() ) )
1757 {
1758 if( i == extraPlates.size() )
1759 {
1760 // check if this a stylerepository item
1761 bool inRepo = false;
1762 if( devn->NrOfComponents() == 1 && devn->OwnerCount() == 1 )
1763 for( int32_t r = 0 ; r != doc->GetRepositoryStyleCount(); ++r )
1764 {
1765 PDF::Style* s = doc->GetRepositoryStyle( r );
1766 if( s->ResourceType() == PDF::Solid::eResourceType )
1767 {
1768 if( devn->GetResourceID() == PDF::SolidPtr(s)->GetSpace()->GetResourceID() )
1769 inRepo = true;
1770 }
1771 }
1772
1773 if( inRepo == false )
1774 {
1775 colorant = PDF::Document::Colorant();
1776 colorant.name = n;
1777 extraPlates.push_back( colorant );
1778 colorantSpace.push_back( devn->ChannelSpace( j ) );
1779 colorantFunc.push_back( devn->ChannelFunction( j ) );
1780 devNs.push_back( devn );
1781 }
1782 }
1783 else if( colorantSpace[i] == NULL )
1784 {
1785 colorantSpace[i] = devn->ChannelSpace( j );
1786 colorantFunc[i] = devn->ChannelFunction( j );
1787 }
1788 }
1789 }
1790 }
1791 }
1792
1793 if( extraPlates.empty() )
1794 return;
1795
1796 mProofProfile->Open( PDF::CMM::eInput, PDF::eAbsoluteColorimetric );
1797 PDF::CMM* proofCMM = mProofProfile->GetCMM( PDF::CMM::eInput, PDF::eAbsoluteColorimetric );
1798
1799 PDF::LookupSpotColor fe;
1800 for( size_t col = 0; col != extraPlates.size(); ++col )
1801 {
1802 colorant = extraPlates[col];
1803 double tr[MAX_CHANNELS];
1804 if( colorantSpace[col] )
1805 {
1806 double in = 1;
1807 colorantFunc[col]->Transform( &in, tr );
1808 DetermineCIELab( tr, colorantSpace[col], proofCMM, colorant.lab );
1809 }
1810 else
1811 {
1812 if( fe.FindInLibraries( colorant.name ) && fe.mSpace == PDF::LABSpace )
1813 { // color in Lab
1814 colorant.lab[0] = fe.mLab[ 0 ];
1815 colorant.lab[1] = fe.mLab[ 1 ];
1816 colorant.lab[2] = fe.mLab[ 2 ];
1817 }
1818 else
1819 {
1820 if( !::stricmp( colorant.name, "Red" ) ||
1821 !::stricmp( colorant.name, "Green" ) ||
1822 !::stricmp( colorant.name, "Blue" ) )
1823 {
1824 uint16_t rgb16[3] = { 0, 0, 0 };
1825 if( !::stricmp( colorant.name, "Red" ) )
1826 rgb16[1] = rgb16[2] = PDF::Component1;
1827 else if( !::stricmp( colorant.name, "Green" ) )
1828 rgb16[0] = rgb16[2] = PDF::Component1;
1829 else
1830 rgb16[0] = rgb16[1] = PDF::Component1;
1831 double lab[3];
1832 doc->GetDeviceCMYKColorSpace()->GetCIELab( rgb16, lab );
1833 colorant.lab[0] = float( lab[0] );
1834 colorant.lab[1] = float( lab[1] );
1835 colorant.lab[2] = float( lab[2] );
1836 }
1837 else
1838 {
1839 PDF::DeviceN* devN = devNs[ col ];
1840
1841 double in[MAX_CHANNELS];
1842 ::memset( in, 0, MAX_CHANNELS * sizeof( double ) );
1843
1844 uint32_t index = 0;
1845 while( ::stricmp( devN->ChannelName( index ), colorant.name ) )
1846 ++index;
1847 in[ index ] = 1;
1848
1849 devN->GetFunction()->Transform( in, tr );
1850 DetermineCIELab( tr, devN->GetAlternate(), proofCMM, colorant.lab );
1851 }
1852 }
1853 }
1854 if( devNCnt == 1 )
1855 {
1856 if( doc->GetPageCount() == 1 &&
1857 doc->GetPage()->GetFirstLayer()->GetNext() == NULL &&
1858 doc->GetPage()->GetFirstLayer()->GetFirstChild() &&
1859 doc->GetPage()->GetFirstLayer()->GetFirstChild()->GetNext() == NULL &&
1860 doc->GetPage()->GetFirstLayer()->GetFirstChild()->GetType() == PDF::gRasterType )
1861 {
1862 PDF::RasterPtr ras = (PDF::RasterPtr)doc->GetPage()->GetFirstLayer()->GetFirstChild();
1863 XML* xml = ras->MetaData( false );
1864 if( xml )
1865 {
1866 for( auto& chan : *xml->root->GetNodeByName( "ChannelInfo" ) )
1867 if( chan.GetAttributeByName( "Name" )->ToString() == colorant.name )
1868 colorant.solidity = chan.GetNodeByName( "Opacity" )->ToFloat();
1869 }
1870 }
1871 }
1872
1873 uint32_t cIdx = 0;
1874 while( cIdx != doc->colorantCount() )
1875 {
1876 if( ::stricmp( doc->colorant( cIdx ).name, colorant.name ) == 0 )
1877 break;
1878 ++cIdx;
1879 }
1880 doc->setColorant( cIdx, colorant );
1881 }
1882
1883 mProofProfile->Close( PDF::CMM::eInput, PDF::eAbsoluteColorimetric );
1884}
1885
1886void ThumbnailRenderer::AddDocumentColorants( PDF::Document* doc )
1887{
1888 //
1889 // Determine the extra channels based on the proof profile and colorant in the document
1890 //
1891 uint32_t i;
1892 size_t j;
1893 int32_t profileChannelCount = ColorSpaceComponents( mProofProfile->GetSpace() );
1894 for( i = 0; i != doc->colorantCount() && i < uint32_t( doc->GetInstance()->GetMaxChannels() - profileChannelCount ); ++i )
1895 {
1896 bool found = false;
1897 const PDF::Document::Colorant& colorant = doc->colorant(i);
1898 if( mProofProfile->GetSpace() == PDF::GraySpace || mProofProfile->GetSpace() == PDF::RGBSpace )
1899 found = true;
1900 else
1901 for( int32_t ch = 0; !found && ch != profileChannelCount; ++ch )
1902 {
1903 ACPL::String profChannelName = mProofProfile->GetChannel( ch );
1904 found = profChannelName == colorant.name;
1905 }
1906 if( !found )
1907 {
1908 j = mExtraPlates.size();
1909 mExtraPlates.push_back( colorant.name );
1910
1911 uint8_t rgb8[3];
1912 uint16_t labV[3];
1913 RGBValues channelRGB;
1914
1915 double dL = ( ( colorant.lab[0] / 100 ) - 1 ) * 0xFF;
1916 double da = colorant.lab[1];
1917 double db = colorant.lab[2];
1918 uint32_t v;
1919 if( colorant.tvi == 0 )
1920 {
1921 for( v = 0; v != 256; ++v )
1922 {
1923 labV[0] = uint16_t( 0xFF00 + v * dL );
1924 labV[1] = uint16_t( 0x8000 + v * da );
1925 labV[2] = uint16_t( 0x8000 + v * db );
1926 mDeviceCMM->Lab2Image( labV, rgb8, 1 );
1927 channelRGB.v[v][0] = rgb8[ 0 ];
1928 channelRGB.v[v][1] = rgb8[ 1 ];
1929 channelRGB.v[v][2] = rgb8[ 2 ];
1930 }
1931 }
1932 else
1933 {
1934 for( v = 0; v != 256; ++v )
1935 {
1936 double percentage = v / 255.0;
1937 percentage += sin( percentage * M_PI ) * colorant.tvi;
1938 if( percentage < 0 )
1939 percentage = 0;
1940 else if( percentage > 1 )
1941 percentage = 255;
1942 else
1943 percentage *= 255;
1944 labV[0] = uint16_t( 0xFF00 + percentage * dL );
1945 labV[1] = uint16_t( 0x8000 + percentage * da );
1946 labV[2] = uint16_t( 0x8000 + percentage * db );
1947 mDeviceCMM->Lab2Image( labV, rgb8, 1 );
1948 channelRGB.v[v][0] = rgb8[ 0 ];
1949 channelRGB.v[v][1] = rgb8[ 1 ];
1950 channelRGB.v[v][2] = rgb8[ 2 ];
1951 }
1952 }
1953 mChannelRGB.push_back( channelRGB );
1954 mChannelSolidity[j] = colorant.solidity;
1955 }
1956 }
1957}
1958
1959void ThumbnailRenderer::CopyBitsDeviceN( uint8_t* dest, const PDF::LRectangle& updateBounds, int32_t rowBytes, bool bImageAlphaBlending )
1960{
1961 int32_t w = updateBounds.right - updateBounds.left;
1962 uint8_t* src;
1963 uint8_t* srcX;
1964 uint8_t* destX;
1965 uint8_t* srcAlpha;
1966 uint16_t alpha;
1967 uint16_t backDrop;
1968 int32_t top = updateBounds.top - int32_t( mState.mapping.map[2][1] );
1969 int32_t bottom = top + updateBounds.bottom - updateBounds.top;
1970 int32_t left = updateBounds.left - int32_t( mState.mapping.map[2][0] );
1971 int32_t right = left + w;
1972 int32_t c;
1973 int32_t x, y;
1974 int32_t components = mComponents;
1975 int32_t profComponents = ColorSpaceComponents( mProofProfile->GetSpace() );
1976 bool showBack = false;
1977 bool mask[MAX_CHANNELS];
1978
1979 for( c = 0, x = 0; c != components; ++c )
1980 if( ( mask[c] = ( ( 1ll << c ) & mVisiblePlates ) == 0 ) == false )
1981 ++x;
1982 if( x == 1 )
1983 {
1984 CopyBitsSingleChannel( dest, updateBounds, rowBytes, bImageAlphaBlending );
1985 return;
1986 }
1987 uint16_t* lab = new uint16_t[ w * 3 ];
1988 uint8_t* blended = new uint8_t[ w * ( mComponents > 3 ? mComponents : 3 ) ];
1989
1990 src = mDestImage + updateBounds.top * mDestLineBytes + updateBounds.left * mDestPixelBytes;
1991 //dest += updateBounds.top * rowBytes + updateBounds.left * 4;
1992 for( y = top; y != bottom; ++y )
1993 {
1994 srcX = src;
1995 destX = blended;
1996 if( mIsSubstractive )
1997 {
1998 for( x = left; x != right; ++x )
1999 {
2000 if( ( alpha = srcX[components] ) == 255 )
2001 {
2002 for( c = 0; c != profComponents; ++c )
2003 {
2004 *destX = mask[c] ? 0 : *srcX;
2005 ++destX;
2006 ++srcX;
2007 }
2008 srcX += components - profComponents;
2009 }
2010 else if( alpha == 0 )
2011 {
2012 for( c = 0; c != profComponents; ++c )
2013 *destX++ = 0;
2014 srcX += components;
2015 }
2016 else
2017 {
2018 for( c = 0; c != profComponents; ++c )
2019 {
2020 *destX = mask[c] ? 0 : uint8_t( ( *srcX * alpha ) >> 8 );
2021 ++destX;
2022 ++srcX;
2023 }
2024 srcX += components - profComponents;
2025 }
2026 ++srcX;
2027 }
2028 }
2029 else
2030 {
2031 for( x = left; x != right; ++x )
2032 {
2033 if( ( alpha = srcX[components] ) == 255 )
2034 {
2035 for( c = 0; c != profComponents; ++c )
2036 {
2037 *destX = mask[c] ? 0 : *srcX;
2038 ++destX;
2039 ++srcX;
2040 }
2041 srcX += components - profComponents;
2042 }
2043 else if( alpha == 0 )
2044 {
2045 for( c = 0; c != profComponents; ++c )
2046 *destX++ = 255;
2047 srcX += components;
2048 }
2049 else
2050 {
2051 backDrop = ( 255 - alpha ) << 8;
2052 for( c = 0; c != profComponents; ++c )
2053 {
2054 *destX = mask[c] ? 0 : uint8_t( ( backDrop + *srcX * alpha ) >> 8 );
2055 ++destX;
2056 ++srcX;
2057 }
2058 srcX += components - profComponents;
2059 }
2060 ++srcX;
2061 }
2062 }
2063 //
2064 // Convert from Proof ICC space to Screen RGB
2065 //
2066 mProofProfile->Image2Lab( mProofIntent, blended, lab, w );
2067 mDeviceCMM->Lab2Image( lab, blended, w );
2068 //
2069 // Mix in spot colors
2070 //
2071 for( c = profComponents; c != components; ++c )
2072 if( mask[c] == false )
2073 {
2074 const uint8_t (*mul)[256] = GetMUL8();
2075 const RGBValues& component = mChannelRGB[c-profComponents];
2076 srcX = src + c;
2077 srcAlpha = src + components;
2078 destX = blended;
2079 if( mChannelSolidity[c-profComponents] == 0 )
2080 {
2081 for( x = left; x != right; ++x )
2082 {
2083 if( ( alpha = *srcAlpha ) == 255 )
2084 alpha = *srcX;
2085 else if( alpha != 0 )
2086 alpha = ( *srcX * alpha ) >> 8;
2087 uint8_t srcR = component.v[alpha][0];
2088 uint8_t srcG = component.v[alpha][1];
2089 uint8_t srcB = component.v[alpha][2];
2090 srcX += components + 1;
2091 srcAlpha += components + 1;
2092 *destX = mul[ srcR ][ *destX ];
2093 ++destX;
2094 *destX = mul[ srcG ][ *destX ];
2095 ++destX;
2096 *destX = mul[ srcB ][ *destX ];
2097 ++destX;
2098 }
2099 }
2100 else
2101 {
2102 double solidity = mChannelSolidity[c-profComponents];
2103 double invSolidity;
2104 for( x = left; x != right; ++x )
2105 {
2106 if( ( alpha = *srcAlpha ) == 255 )
2107 alpha = *srcX;
2108 else if( alpha != 0 )
2109 alpha = ( *srcX * alpha ) >> 8;
2110 uint8_t srcR = component.v[alpha][0];
2111 uint8_t srcG = component.v[alpha][1];
2112 uint8_t srcB = component.v[alpha][2];
2113 srcX += components + 1;
2114 srcAlpha += components + 1;
2115
2116 // Solidity formula
2117 // Solidity = 0 -> Full mixing with background
2118 // Solidity = 1 -> Full inkcoverage, background invisible
2119 //
2120 // Effective solidity is related to coverage of ink (ink amount)
2121 // S2 = Solidity * coverage (= *srcX)
2122 // Background contribution is inverse of the solidity
2123 // Background contribution = 1 - S2
2124 //
2125 if( ( invSolidity = 1 - ( solidity * alpha / 255 ) ) == 1 )
2126 {
2127 *destX = mul[ srcR ][ *destX ];
2128 ++destX;
2129 *destX = mul[ srcG ][ *destX ];
2130 ++destX;
2131 *destX = mul[ srcB ][ *destX ];
2132 ++destX;
2133 }
2134 else
2135 {
2136 *destX = mul[ srcR ][ 255 - uint8_t( ( 255 - *destX ) * invSolidity ) ];
2137 ++destX;
2138 *destX = mul[ srcG ][ 255 - uint8_t( ( 255 - *destX ) * invSolidity ) ];
2139 ++destX;
2140 *destX = mul[ srcB ][ 255 - uint8_t( ( 255 - *destX ) * invSolidity ) ];
2141 ++destX;
2142 }
2143 }
2144 }
2145 }
2146 //
2147 // Copy and blend with alpha
2148 //
2149 srcX = blended;
2150 destX = dest;
2151 srcAlpha = src + components;
2152 for( x = left; x != right; ++x )
2153 {
2154 *destX++ = *srcX++;
2155 *destX++ = *srcX++;
2156 *destX++ = *srcX++;
2157 if( !bImageAlphaBlending )
2158 *destX++ = *srcAlpha;
2159 srcAlpha += components + 1;
2160 }
2161 src += mDestLineBytes;
2162 dest += rowBytes;
2163 }
2164 delete[] blended;
2165 delete[] lab;
2166}
2167
2168void ThumbnailRenderer::CopyBitsNoMatch( uint8_t* dest, const PDF::LRectangle& updateBounds, int32_t rowBytes, bool bImageAlphaBlending )
2169{
2170 if( ( mVisiblePlates&7 ) == ( 1 << 0 ) || ( mVisiblePlates&7 ) == ( 1 << 1 ) || ( mVisiblePlates&7 ) == ( 1 << 2 ) )
2171 {
2172 CopyBitsSingleChannel( dest, updateBounds, rowBytes, bImageAlphaBlending );
2173 return;
2174 }
2175
2176 uint8_t* src;
2177 uint8_t* srcX;
2178 uint8_t* destX;
2179 uint16_t alpha;
2180 uint16_t backDrop;
2181 bool showBack = false;
2182 bool mask[3];
2183 int32_t top = updateBounds.top - int32_t( mState.mapping.map[2][1] );
2184 int32_t bottom = top + updateBounds.bottom - updateBounds.top;
2185 int32_t left = updateBounds.left - int32_t( mState.mapping.map[2][0] );
2186 int32_t right = left + updateBounds.right - updateBounds.left;
2187
2188 mask[0] = ( ( 1 << 0 ) & mVisiblePlates ) == 0;
2189 mask[1] = ( ( 1 << 1 ) & mVisiblePlates ) == 0;
2190 mask[2] = ( ( 1 << 2 ) & mVisiblePlates ) == 0;
2191
2192 src = mDestImage + updateBounds.top * mDestLineBytes + updateBounds.left * mDestPixelBytes;
2193 //dest += updateBounds.top * rowBytes + updateBounds.left * 4;
2194 for( int32_t y = top; y != bottom; ++y )
2195 {
2196 srcX = src;
2197 destX = dest;
2198 src += mDestLineBytes;
2199 dest += rowBytes;
2200 for( int32_t x = left; x != right; ++x )
2201 {
2202 if( !bImageAlphaBlending )
2203 {
2204 *destX++ = *srcX++;
2205 *destX++ = *srcX++;
2206 *destX++ = *srcX++;
2207 *destX++ = *srcX++;
2208 }
2209 else
2210 {
2211 if( ( alpha = srcX[3] ) == 255 )
2212 {
2213 *destX++ = mask[0] ? 0 : srcX[0];
2214 *destX++ = mask[1] ? 0 : srcX[1];
2215 *destX++ = mask[2] ? 0 : srcX[2];
2216 }
2217 else if( alpha == 0 )
2218 {
2219 register uint8_t backDrop8;
2220 if( showBack && ( ( ( x >> 3 ) + ( y >> 3 ) ) & 1 ) == 0 )
2221 backDrop8 = 191;
2222 else
2223 backDrop8 = 255;
2224 *destX++ = backDrop8;
2225 *destX++ = backDrop8;
2226 *destX++ = backDrop8;
2227 }
2228 else
2229 {
2230 if( showBack && ( ( ( x >> 3 ) + ( y >> 3 ) ) & 1 ) == 0 )
2231 backDrop = ( 255 - alpha ) * 192;
2232 else
2233 backDrop = ( 255 - alpha ) << 8;
2234 *destX++ = uint8_t( ( backDrop + ( mask[0] ? 0 : srcX[0] ) * alpha ) >> 8 );
2235 *destX++ = uint8_t( ( backDrop + ( mask[1] ? 0 : srcX[1] ) * alpha ) >> 8 );
2236 *destX++ = uint8_t( ( backDrop + ( mask[2] ? 0 : srcX[2] ) * alpha ) >> 8 );
2237 }
2238 srcX += 4;
2239 }
2240 }
2241 }
2242}
2243
2244void ThumbnailRenderer::CopyBitsICC( uint8_t* dest, const PDF::LRectangle& updateBounds, int32_t rowBytes, bool bImageAlphaBlending )
2245{
2246 int32_t w = updateBounds.right - updateBounds.left;
2247 uint8_t* src;
2248 uint8_t* srcX;
2249 uint8_t* destX;
2250 uint8_t* srcAlpha;
2251 uint16_t alpha;
2252 uint16_t backDrop;
2253 int32_t top = updateBounds.top - int32_t( mState.mapping.map[2][1] );
2254 int32_t bottom = top + updateBounds.bottom - updateBounds.top;
2255 int32_t left = updateBounds.left - int32_t( mState.mapping.map[2][0] );
2256 int32_t right = left + w;
2257 int32_t c;
2258 int32_t x, y;
2259 int32_t components = mComponents;
2260 bool mask[MAX_CHANNELS];
2261
2262 for( c = 0, x = 0; c != components; ++c )
2263 if( ( mask[c] = ( int64_t( 1LL << c ) & mVisiblePlates ) == 0 ) == false )
2264 ++x;
2265 if( x == 1 )
2266 {
2267 CopyBitsSingleChannel( dest, updateBounds, rowBytes, bImageAlphaBlending );
2268 return;
2269 }
2270 uint16_t* lab = new uint16_t[ w * 3 ];
2271 uint8_t* blended = new uint8_t[ w * ( mComponents > 3 ? mComponents : 3 ) ];
2272
2273 src = mDestImage + updateBounds.top * mDestLineBytes + updateBounds.left * mDestPixelBytes;
2274 //dest += updateBounds.top * rowBytes + updateBounds.left * 4;
2275 for( y = top; y != bottom; ++y )
2276 {
2277 srcX = src;
2278 destX = blended;
2279 if( mIsSubstractive )
2280 {
2281 for( x = left; x != right; ++x )
2282 {
2283 if( ( alpha = srcX[components] ) == 255 )
2284 {
2285 for( c = 0; c != components; ++c )
2286 {
2287 *destX = mask[c] ? 0 : *srcX;
2288 ++destX;
2289 ++srcX;
2290 }
2291 }
2292 else if( alpha == 0 )
2293 {
2294 for( c = 0; c != components; ++c )
2295 *destX++ = 0;
2296 srcX += components;
2297 }
2298 else
2299 {
2300 for( c = 0; c != components; ++c )
2301 {
2302 *destX = mask[c] ? 0 : uint8_t( ( *srcX * alpha ) >> 8 );
2303 ++destX;
2304 ++srcX;
2305 }
2306 }
2307 ++srcX;
2308 }
2309 }
2310 else
2311 {
2312 for( x = left; x != right; ++x )
2313 {
2314 if( ( alpha = srcX[components] ) == 255 )
2315 {
2316 for( c = 0; c != components; ++c )
2317 {
2318 *destX = mask[c] ? 0 : *srcX;
2319 ++destX;
2320 ++srcX;
2321 }
2322 }
2323 else if( alpha == 0 )
2324 {
2325 for( c = 0; c != components; ++c )
2326 *destX++ = 255;
2327 srcX += components;
2328 }
2329 else
2330 {
2331 backDrop = ( 255 - alpha ) << 8;
2332 for( c = 0; c != components; ++c )
2333 {
2334 *destX = mask[c] ? 0 : uint8_t( ( backDrop + *srcX * alpha ) >> 8 );
2335 ++destX;
2336 ++srcX;
2337 }
2338 }
2339 ++srcX;
2340 }
2341 }
2342 mProofProfile->Image2Lab( mProofIntent, blended, lab, w );
2343 mDeviceCMM->Lab2Image( lab, blended, w );
2344 srcX = blended;
2345 destX = dest;
2346 srcAlpha = src + components;
2347 for( x = left; x != right; ++x )
2348 {
2349 *destX++ = *srcX++;
2350 *destX++ = *srcX++;
2351 *destX++ = *srcX++;
2352 if( !bImageAlphaBlending )
2353 *destX++ = *srcAlpha;
2354 srcAlpha += components + 1;
2355 }
2356 src += mDestLineBytes;
2357 dest += rowBytes;
2358 }
2359 delete[] blended;
2360 delete[] lab;
2361}
2362
2363void ThumbnailRenderer::CopyBitsSingleChannel( uint8_t* dest, const PDF::LRectangle& updateBounds, int32_t rowBytes, bool bImageAlphaBlending )
2364{
2365 uint8_t* src;
2366 uint8_t* srcX;
2367 uint8_t* destX;
2368 uint16_t alpha;
2369 bool showBack = false;
2370 int32_t top = updateBounds.top - int32_t( mState.mapping.map[2][1] );
2371 int32_t bottom = top + updateBounds.bottom - updateBounds.top;
2372 int32_t left = updateBounds.left - int32_t( mState.mapping.map[2][0] );
2373 int32_t right = left + updateBounds.right - updateBounds.left;
2374 int32_t components = mComponents;
2375 int32_t c;
2376 register uint8_t backDrop8;
2377
2378 for( c = 0; c != components; ++c )
2379 if( ( int64_t( 1LL << c ) & mVisiblePlates ) != 0 )
2380 break;
2381
2382 src = mDestImage + updateBounds.top * mDestLineBytes + updateBounds.left * mDestPixelBytes;
2383 //dest += updateBounds.top * rowBytes + updateBounds.left * 4;
2384 for( int32_t y = top; y != bottom; ++y )
2385 {
2386 srcX = src;
2387 destX = dest;
2388 src += mDestLineBytes;
2389 dest += rowBytes;
2390 if( mIsSubstractive )
2391 {
2392 for( int32_t x = left; x != right; ++x )
2393 {
2394 if( ( alpha = srcX[components] ) == 255 )
2395 backDrop8 = 255 - srcX[c];
2396 else if( alpha == 0 )
2397 {
2398 if( showBack && ( ( ( x >> 3 ) + ( y >> 3 ) ) & 1 ) == 0 )
2399 backDrop8 = 191;
2400 else
2401 backDrop8 = 255;
2402 }
2403 else
2404 {
2405 register uint16_t backDrop;
2406 if( showBack && ( ( ( x >> 3 ) + ( y >> 3 ) ) & 1 ) == 0 )
2407 backDrop = ( 255 - alpha ) * 192;
2408 else
2409 backDrop = ( 255 - alpha ) << 8;
2410 backDrop8 = uint8_t( ( backDrop + ( 255 - srcX[c] ) * alpha ) >> 8 );
2411 }
2412 *destX++ = backDrop8;
2413 *destX++ = backDrop8;
2414 *destX++ = backDrop8;
2415
2416 if( !bImageAlphaBlending )
2417 {
2418 *destX++ = 255;
2419 }
2420
2421 srcX += mDestPixelBytes;
2422 }
2423 }
2424 else
2425 {
2426 for( int32_t x = left; x != right; ++x )
2427 {
2428 if( ( alpha = srcX[components] ) == 255 )
2429 backDrop8 = srcX[c];
2430 else if( alpha == 0 )
2431 {
2432 if( showBack && ( ( ( x >> 3 ) + ( y >> 3 ) ) & 1 ) == 0 )
2433 backDrop8 = 191;
2434 else
2435 backDrop8 = 255;
2436 }
2437 else
2438 {
2439 register uint16_t backDrop;
2440 if( showBack && ( ( ( x >> 3 ) + ( y >> 3 ) ) & 1 ) == 0 )
2441 backDrop = ( 255 - alpha ) * 192;
2442 else
2443 backDrop = ( 255 - alpha ) << 8;
2444 backDrop8 = uint8_t( ( backDrop + srcX[c] * alpha ) >> 8 );
2445 }
2446 *destX++ = backDrop8;
2447 *destX++ = backDrop8;
2448 *destX++ = backDrop8;
2449
2450 if( !bImageAlphaBlending )
2451 {
2452 *destX++ = 255;
2453 }
2454
2455 srcX += mDestPixelBytes;
2456 }
2457 }
2458 }
2459}
2460
2461void ThumbnailRenderer::CopyBits( uint8_t* dest, const PDF::LRectangle& updateBounds, int32_t rowBytes, bool bImageAlphaBlending )
2462{
2463 switch( mWhichCopy )
2464 {
2465 case eNoMatch :
2466 CopyBitsNoMatch( dest, updateBounds, rowBytes, bImageAlphaBlending );
2467 break;
2468 case eDeviceN :
2469 CopyBitsDeviceN( dest, updateBounds, rowBytes, bImageAlphaBlending );
2470 break;
2471 case eICC :
2472 CopyBitsICC( dest, updateBounds, rowBytes, bImageAlphaBlending );
2473 break;
2474 }
2475}
2476
2477void ThumbnailRenderer::SetVisiblePlates( uint64_t mask )
2478{
2479 mVisiblePlates = mask;
2480}
2481
PDF Document.
bool RenderTiles(const TileSettings &tileSettings, const aur::ACPL::FileSpec &outputPath, int16_t channelIndex, const aur::ACPL::UString &multichannelRender)
void AssignInputProfiles(const aur::ACPL::FileSpec &rgbProfile, const aur::ACPL::FileSpec &cmykProfile, const aur::ACPL::FileSpec &grayProfile, int32_t intent)
bool Render(const aur::ACPL::FileSpec &outputPath, const char *thumbnailType, int32_t x, int32_t y, int32_t width, int32_t height, uint32_t quality=100, uint64_t visiblePlates=UINT64_MAX)
bool Setup(const aur::ACPL::FileSpec &profileFile, const aur::ACPL::FileSpec &cmmFile)
const char * GetDocumentChannelName(uint32_t index)
AOI_SoftProofRenderer(AOI_Document *document)
void UpdateRenderer(uint32_t width, uint32_t height)