1 //------------------------------------------------------------------------------
2 // emPsRenderer.cpp
3 //
4 // Copyright (C) 2006-2011,2014,2017-2019 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20
21 #include <emPs/emPsRenderer.h>
22 #include <emCore/emInstallInfo.h>
23
24
Acquire(emRootContext & rootContext)25 emRef<emPsRenderer> emPsRenderer::Acquire(emRootContext & rootContext)
26 {
27 EM_IMPL_ACQUIRE_COMMON(emPsRenderer,rootContext,"");
28 }
29
30
StartJob(const emPsDocument & document,int pageIndex,emImage & outputImage,double priority,emEngine * listenEngine)31 emPsRenderer::JobHandle emPsRenderer::StartJob(
32 const emPsDocument & document, int pageIndex,
33 emImage & outputImage, double priority,
34 emEngine * listenEngine
35 )
36 {
37 Job * job;
38
39 job=new Job;
40 job->Document=document;
41 job->PageIndex=pageIndex;
42 job->Image=&outputImage;
43 job->Priority=priority;
44 job->ListenEngine=listenEngine;
45 job->State=JS_WAITING;
46 job->Prev=NULL;
47 job->Next=NULL;
48 AddToJobList(job);
49 PSPriorityValid=false;
50 WakeUp();
51 return job;
52 }
53
54
SetJobPriority(JobHandle jobHandle,double priority)55 void emPsRenderer::SetJobPriority(JobHandle jobHandle, double priority)
56 {
57 Job * job;
58
59 job=(Job*)jobHandle;
60 if (job->Priority!=priority) {
61 job->Priority=priority;
62 if (job->State==JS_WAITING) {
63 PSPriorityValid=false;
64 WakeUp();
65 }
66 }
67 }
68
69
CloseJob(JobHandle jobHandle)70 void emPsRenderer::CloseJob(JobHandle jobHandle)
71 {
72 Job * job;
73
74 job=(Job*)jobHandle;
75 if (job->State!=JS_SUCCESS && job->State!=JS_ERROR) {
76 job->ListenEngine=NULL;
77 SetJobState(job,JS_ERROR);
78 }
79 delete job;
80 }
81
82
emPsRenderer(emContext & context,const emString & name)83 emPsRenderer::emPsRenderer(emContext & context, const emString & name)
84 : emModel(context,name),
85 Timer(GetScheduler()),
86 PSAgent(*this)
87 {
88 SetMinCommonLifetime(5);
89 PSPriorityValid=false;
90 FirstJob=NULL;
91 LastJob=NULL;
92 MainState=COLD_WAIT_JOB;
93 CurrentJob=NULL;
94 CurrentPageIndex=0;
95 AddWakeUpSignal(Timer.GetSignal());
96 }
97
98
~emPsRenderer()99 emPsRenderer::~emPsRenderer()
100 {
101 while (FirstJob) CloseJob(FirstJob);
102 if (CurrentJob) CloseJob(CurrentJob);
103 Process.Terminate();
104 }
105
106
Cycle()107 bool emPsRenderer::Cycle()
108 {
109 bool busy,readProceeded,writeProceeded;
110 Job * job;
111 int flags;
112
113 busy=false;
114
115 switch (MainState) {
116
117 L_ENTER_COLD_WAIT_JOB:
118 CurrentDocument.Clear();
119 PSAgent.ReleaseAccess();
120 MainState=COLD_WAIT_JOB;
121 case COLD_WAIT_JOB:
122 if (FirstJob) goto L_ENTER_COLD_WAIT_ACCESS;
123 break;
124
125 L_ENTER_COLD_WAIT_ACCESS:
126 UpdatePSPriority();
127 PSAgent.RequestAccess();
128 MainState=COLD_WAIT_ACCESS;
129 case COLD_WAIT_ACCESS:
130 if (!FirstJob) goto L_ENTER_COLD_WAIT_JOB;
131 if (PSAgent.HasAccess()) goto L_ENTER_PREPARE_PROCESS;
132 UpdatePSPriority();
133 break;
134
135 L_ENTER_PREPARE_PROCESS:
136 job=SearchBestJob();
137 if (!job) goto L_ENTER_COLD_WAIT_JOB;
138 CurrentDocument=job->Document;
139 try {
140 TryStartProcess();
141 }
142 catch (const emException & exception) {
143 FailAllJobs(exception.GetText());
144 goto L_ENTER_COLD_WAIT_JOB;
145 }
146 PrepareWritingStartup();
147 PrepareReadingStartup();
148 Timer.Start(12000);
149 MainState=PREPARE_PROCESS;
150 case PREPARE_PROCESS:
151 if (!Process.IsRunning()) {
152 FailDocJobs("PostScript interpretation failed: Interpreter exited.");
153 goto L_ENTER_QUIT_PROCESS;
154 }
155 try {
156 TryRead();
157 TryWrite();
158 }
159 catch (const emException & exception) {
160 FailDocJobs(exception.GetText());
161 goto L_ENTER_QUIT_PROCESS;
162 }
163 if (IsReadingFinished()) goto L_ENTER_RUN_JOB;
164 if (!Timer.IsRunning()) {
165 FailDocJobs("PostScript interpretation failed: Start-up timed out.");
166 goto L_ENTER_QUIT_PROCESS;
167 }
168 busy=true;
169 break;
170
171 L_ENTER_RUN_JOB:
172 if (CurrentDocument.GetDataRefCount()<=1) goto L_ENTER_QUIT_PROCESS;
173 job=SearchBestSameDocJob();
174 if (!job) goto L_ENTER_QUIT_PROCESS;
175 SetJobState(job,JS_RUNNING);
176 CurrentPageIndex=CurrentJob->PageIndex;
177 PrepareWritingPage();
178 PrepareReadingPage();
179 Timer.Start(8000);
180 MainState=RUN_JOB;
181 case RUN_JOB:
182 if (!Process.IsRunning()) {
183 FailDocJobs("PostScript interpretation failed: Interpreter exited.");
184 goto L_ENTER_QUIT_PROCESS;
185 }
186 if (!Timer.IsRunning()) {
187 FailDocJobs("PostScript interpretation failed: Page timed out.");
188 goto L_ENTER_QUIT_PROCESS;
189 }
190 for (;;) {
191 try {
192 readProceeded=TryRead();
193 writeProceeded=TryWrite();
194 }
195 catch (const emException & exception) {
196 FailDocJobs(exception.GetText());
197 goto L_ENTER_QUIT_PROCESS;
198 }
199 if (IsReadingFinished()) {
200 if (CurrentJob) SetJobState(CurrentJob,JS_SUCCESS);
201 goto L_ENTER_HOT_WAIT_JOB;
202 }
203 if (IsTimeSliceAtEnd()) break;
204 if (!readProceeded && !writeProceeded) {
205 flags=emProcess::WF_WAIT_STDOUT;
206 if (!IsWritingFinished()) flags|=emProcess::WF_WAIT_STDIN;
207 Process.WaitPipes(flags,10);
208 }
209 }
210 busy=true;
211 break;
212
213 L_ENTER_HOT_WAIT_JOB:
214 PSAgent.ReleaseAccess();
215 Timer.Start(3000);
216 MainState=HOT_WAIT_JOB;
217 case HOT_WAIT_JOB:
218 if (CurrentDocument.GetDataRefCount()<=1) goto L_ENTER_QUIT_PROCESS;
219 if (FirstJob) goto L_ENTER_HOT_WAIT_ACCESS;
220 if (!Timer.IsRunning()) goto L_ENTER_QUIT_PROCESS;
221 busy=true;
222 break;
223
224 L_ENTER_HOT_WAIT_ACCESS:
225 UpdatePSPriority();
226 PSAgent.RequestAccess();
227 MainState=HOT_WAIT_ACCESS;
228 case HOT_WAIT_ACCESS:
229 if (CurrentDocument.GetDataRefCount()<=1) goto L_ENTER_QUIT_PROCESS;
230 if (!FirstJob) goto L_ENTER_QUIT_PROCESS;
231 if (PSAgent.HasAccess()) goto L_ENTER_RUN_JOB;
232 UpdatePSPriority();
233 busy=true;
234 break;
235
236 L_ENTER_QUIT_PROCESS:
237 CurrentDocument.Clear();
238 PSAgent.ReleaseAccess();
239 Process.CloseWriting();
240 Process.CloseReading();
241 Process.SendTerminationSignal();
242 Timer.Start(10000);
243 MainState=QUIT_PROCESS;
244 case QUIT_PROCESS:
245 if (!Process.IsRunning()) goto L_ENTER_COLD_WAIT_JOB;
246 if (!Timer.IsRunning()) {
247 FailAllJobs(
248 "Failed to terminate PostScript interpreter after previous job."
249 );
250 Timer.Start(10000);
251 }
252 busy=true;
253 break;
254 }
255
256 return busy;
257 }
258
259
AddToJobList(Job * job)260 void emPsRenderer::AddToJobList(Job * job)
261 {
262 job->Prev=LastJob;
263 job->Next=NULL;
264 if (LastJob) LastJob->Next=job; else FirstJob=job;
265 LastJob=job;
266 }
267
268
RemoveFromJobList(Job * job)269 void emPsRenderer::RemoveFromJobList(Job * job)
270 {
271 if (job->Prev) job->Prev->Next=job->Next;
272 else FirstJob=job->Next;
273 if (job->Next) job->Next->Prev=job->Prev;
274 else LastJob=job->Prev;
275 job->Prev=NULL;
276 job->Next=NULL;
277 }
278
279
SearchBestJob()280 emPsRenderer::Job * emPsRenderer::SearchBestJob()
281 {
282 Job * job, * bestJob;
283 double bestPri;
284
285 bestJob=FirstJob;
286 if (bestJob) {
287 bestPri=bestJob->Priority;
288 for (job=bestJob->Next; job; job=job->Next) {
289 if (bestPri<job->Priority) {
290 bestPri=job->Priority;
291 bestJob=job;
292 }
293 }
294 }
295 return bestJob;
296 }
297
298
SearchBestSameDocJob()299 emPsRenderer::Job * emPsRenderer::SearchBestSameDocJob()
300 {
301 Job * job, * bestJob;
302
303 for (bestJob=FirstJob; bestJob; bestJob=bestJob->Next) {
304 if (CurrentDocument==bestJob->Document) break;
305 }
306 if (bestJob) {
307 for (job=bestJob->Next; job; job=job->Next) {
308 if (bestJob->Priority<job->Priority && bestJob->Document==job->Document) {
309 bestJob=job;
310 }
311 }
312 }
313 return bestJob;
314 }
315
316
SetJobState(Job * job,JobState state,emString errorText)317 void emPsRenderer::SetJobState(Job * job, JobState state, emString errorText)
318 {
319 switch (job->State) {
320 case JS_WAITING:
321 RemoveFromJobList(job);
322 PSPriorityValid=false;
323 WakeUp();
324 break;
325 case JS_RUNNING:
326 CurrentJob=NULL;
327 break;
328 default:
329 break;
330 }
331
332 job->State=state;
333 job->ErrorText=errorText;
334 if (job->ListenEngine) job->ListenEngine->WakeUp();
335
336 switch (job->State) {
337 case JS_WAITING:
338 AddToJobList(job);
339 PSPriorityValid=false;
340 WakeUp();
341 break;
342 case JS_RUNNING:
343 CurrentJob=job;
344 break;
345 default:
346 break;
347 }
348 }
349
350
FailCurrentJob(emString errorMessage)351 void emPsRenderer::FailCurrentJob(emString errorMessage)
352 {
353 if (CurrentJob) SetJobState(CurrentJob,JS_ERROR,errorMessage);
354 }
355
356
FailDocJobs(emString errorMessage)357 void emPsRenderer::FailDocJobs(emString errorMessage)
358 {
359 Job * * pJob;
360 Job * job;
361
362 for (pJob=&FirstJob;;) {
363 job=*pJob;
364 if (!job) break;
365 if (job->Document==CurrentDocument) {
366 SetJobState(job,JS_ERROR,errorMessage);
367 }
368 else {
369 pJob=&job->Next;
370 }
371 }
372 if (CurrentJob) SetJobState(CurrentJob,JS_ERROR,errorMessage);
373 }
374
375
FailAllJobs(emString errorMessage)376 void emPsRenderer::FailAllJobs(emString errorMessage)
377 {
378 while (FirstJob) SetJobState(FirstJob,JS_ERROR,errorMessage);
379 if (CurrentJob) SetJobState(CurrentJob,JS_ERROR,errorMessage);
380 }
381
382
UpdatePSPriority()383 void emPsRenderer::UpdatePSPriority()
384 {
385 Job * job;
386 double pri;
387
388 if (!PSPriorityValid) {
389 job=SearchBestJob();
390 if (job) pri=job->Priority;
391 else pri=0.0;
392 PSAgent.SetAccessPriority(pri);
393 PSPriorityValid=true;
394 }
395 }
396
397
TryStartProcess()398 void emPsRenderer::TryStartProcess()
399 {
400 emArray<emString> args;
401
402 #if defined(_WIN32)
403 args.Add(
404 emGetChildPath(
405 emGetInstallPath(EM_IDT_LIB,"emPs","emPs"),
406 "emPsWinAdapterProc"
407 )
408 );
409 const char * p=getenv("EM_DIR");
410 if (!p) throw emException("emPsRenderer: EM_DIR not set.");
411 emString gsPath=emString(p)+"\\thirdparty\\bin\\gs.exe";
412 if (!emIsRegularFile(gsPath)) {
413 // Otherwise we get a quite stupid error message.
414 throw emException("emPsRenderer: Cannot not find %s",gsPath.Get());
415 }
416 args.Add(gsPath);
417 #else
418 args.Add("gs");
419 #endif
420 args.Add("-q");
421 args.Add("-dNOPAUSE");
422 args.Add("-dSAFER");
423 args.Add("-sDEVICE=ppmraw");
424 args.Add("-dTextAlphaBits=1"); // emPsPagePanel performs some
425 args.Add("-dGraphicsAlphaBits=1"); // kind of anti-aliasing. Therefore
426 args.Add("-dNOINTERPOLATE"); // it's disabled here.
427 args.Add("-dAlignToPixels=0");
428 args.Add("-r72.0x72.0"); // Dummy values (adapted for each page by commands).
429 args.Add("-g612x792"); // Dummy values (adapted for each page by commands).
430 args.Add("-sOutputFile=-");
431 args.Add("-_"); // "-" or "-_"?
432
433 Process.TryStart(
434 args,
435 emArray<emString>(),
436 NULL,
437 emProcess::SF_PIPE_STDIN|
438 emProcess::SF_PIPE_STDOUT|
439 emProcess::SF_SHARE_STDERR|
440 emProcess::SF_NO_WINDOW
441 );
442 }
443
444
PrepareWritingStartup()445 void emPsRenderer::PrepareWritingStartup()
446 {
447 WriterState=WRITING_STARTUP;
448 WriterPos=0;
449 WriteCommand.Clear();
450 }
451
452
PrepareWritingPage()453 void emPsRenderer::PrepareWritingPage()
454 {
455 double rx,ry,rt;
456 int w,h,t;
457
458 if (CurrentJob && CurrentJob->Image) {
459 w=CurrentJob->Image->GetWidth();
460 h=CurrentJob->Image->GetHeight();
461 }
462 else {
463 w=10;
464 h=10;
465 }
466 rx=w*72.0/CurrentDocument.GetPageWidth(CurrentPageIndex);
467 ry=h*72.0/CurrentDocument.GetPageHeight(CurrentPageIndex);
468 if (CurrentDocument.IsLandscapePage(CurrentPageIndex)) {
469 rt=rx; rx=ry; ry=rt;
470 t=w; w=h; h=t;
471 }
472 WriteCommand=emString::Format(
473 "\nmark /HWSize [%d %d] /HWResolution [%f %f] currentdevice putdeviceprops pop\n",
474 w,h,
475 rx,ry
476 );
477 WriterState=WRITING_PAGE_SIZE;
478 WriterPos=0;
479 }
480
481
TryWrite()482 bool emPsRenderer::TryWrite()
483 {
484 const char * buf;
485 int len;
486
487 switch (WriterState) {
488 case WRITING_STARTUP:
489 buf=CurrentDocument.GetStartupScriptPtr();
490 len=CurrentDocument.GetStartupScriptLen();
491 if (WriterPos>=len) goto L_ENTER_WRITING_SYNC;
492 break;
493
494 case WRITING_PAGE_SIZE:
495 buf=WriteCommand.Get();
496 len=strlen(buf);
497 if (WriterPos>=len) goto L_ENTER_WRITING_PAGE;
498 break;
499
500 L_ENTER_WRITING_PAGE:
501 WriterPos=0;
502 WriterState=WRITING_PAGE;
503 case WRITING_PAGE:
504 buf=CurrentDocument.GetPageScriptPtr(CurrentPageIndex);
505 len=CurrentDocument.GetPageScriptLen(CurrentPageIndex);
506 if (WriterPos>=len) goto L_ENTER_WRITING_SYNC;
507 break;
508
509 L_ENTER_WRITING_SYNC:
510 WriteCommand=emString::Format(
511 "\n(%s) print\nflush\n",
512 SyncString
513 );
514 WriterPos=0;
515 WriterState=WRITING_SYNC;
516 case WRITING_SYNC:
517 buf=WriteCommand.Get();
518 len=strlen(buf);
519 if (WriterPos>=len) WriterState=WRITING_FINISHED;
520 break;
521
522 default:
523 buf=NULL;
524 len=0;
525 }
526
527 len=Process.TryWrite(buf+WriterPos,len-WriterPos);
528 if (len<0) {
529 throw emException(
530 "PostScript interpretation failed: Interpreter closed STDIN or exited."
531 );
532 }
533 if (len==0) return false;
534 WriterPos+=len;
535 return true;
536 }
537
538
PrepareReadingStartup()539 void emPsRenderer::PrepareReadingStartup()
540 {
541 ReaderState=READING_SYNC;
542 ReadBufferFill=0;
543 RdSyncSearchPos=0;
544 }
545
546
PrepareReadingPage()547 void emPsRenderer::PrepareReadingPage()
548 {
549 ReaderState=READING_IMAGE_HEADER;
550 ReadBufferFill=0;
551 RdSyncSearchPos=0;
552 }
553
554
TryRead()555 bool emPsRenderer::TryRead()
556 {
557 int len,syncLen,eat,r;
558 bool syncFound;
559 const char * p;
560
561 if (ReadBufferFill>=(int)sizeof(ReadBuffer)) {
562 throw emException("PostScript interpretation failed: Read buffer too small.");
563 }
564 len=Process.TryRead(
565 ReadBuffer+ReadBufferFill,
566 sizeof(ReadBuffer)-ReadBufferFill
567 );
568 if (len<0) {
569 throw emException(
570 "PostScript interpretation failed: Interpreter closed STDOUT or exited."
571 );
572 }
573 if (len==0) return false;
574 ReadBufferFill+=len;
575
576 syncLen=strlen(SyncString);
577 syncFound=false;
578 while (RdSyncSearchPos+syncLen<=ReadBufferFill) {
579 p=(const char*)memchr(
580 ReadBuffer+RdSyncSearchPos,
581 SyncString[0],
582 ReadBufferFill-RdSyncSearchPos
583 );
584 if (!p) {
585 RdSyncSearchPos=ReadBufferFill;
586 break;
587 }
588 RdSyncSearchPos=p-ReadBuffer;
589 if (RdSyncSearchPos+syncLen>ReadBufferFill) break;
590 if (memcmp(ReadBuffer+RdSyncSearchPos,SyncString,syncLen)==0) {
591 syncFound=true;
592 break;
593 }
594 RdSyncSearchPos++;
595 }
596
597 eat=0;
598 len=RdSyncSearchPos;
599 switch (ReaderState) {
600 case READING_IMAGE_HEADER:
601 while (len>0) {
602 r=ParseImageHeader(ReadBuffer+eat,len-eat);
603 if (r<0) {
604 eat++;
605 }
606 else if (r==0) {
607 if (len-eat>1024) eat++;
608 else break;
609 }
610 else {
611 eat+=r;
612 goto L_ENTER_READING_IMAGE_DATA;
613 }
614 }
615 break;
616
617 L_ENTER_READING_IMAGE_DATA:
618 RdImgX=0;
619 RdImgY=0;
620 RdImgDone=false;
621 ReaderState=READING_IMAGE_DATA;
622 case READING_IMAGE_DATA:
623 while (len>0) {
624 r=ParseImageData(ReadBuffer+eat,len-eat);
625 if (r<0) {
626 throw emException(
627 "PostScript interpretation failed: Image data confusion."
628 );
629 }
630 else if (r==0) {
631 if (len-eat>1024) {
632 throw emException(
633 "PostScript interpretation failed: Image parser faulty."
634 );
635 }
636 break;
637 }
638 else {
639 eat+=r;
640 }
641 if (RdImgDone) goto L_ENTER_READING_SYNC;
642 }
643 break;
644
645 L_ENTER_READING_SYNC:
646 ReaderState=READING_SYNC;
647 case READING_SYNC:
648 if (syncFound) {
649 ReaderState=READING_FINISHED;
650 eat=ReadBufferFill;
651 }
652 else {
653 eat=RdSyncSearchPos;
654 }
655 break;
656
657 default:
658 eat=ReadBufferFill;
659 break;
660 }
661
662 if (syncFound && ReaderState!=READING_FINISHED) {
663 throw emException(
664 "PostScript interpretation failed: Unsupported document structure."
665 );
666 }
667
668 if (eat>0) {
669 ReadBufferFill-=eat;
670 RdSyncSearchPos-=eat;
671 if (RdSyncSearchPos<0) RdSyncSearchPos=0;
672 if (ReadBufferFill>0) {
673 memmove(ReadBuffer,ReadBuffer+eat,ReadBufferFill);
674 }
675 else {
676 ReadBufferFill=0;
677 }
678 }
679
680 return true;
681 }
682
683
ParseImageHeader(const char * buf,int len)684 int emPsRenderer::ParseImageHeader(const char * buf, int len)
685 {
686 int i,r;
687
688 i=0;
689
690 if (i>=len) return 0;
691 if (buf[i++]!='P') return -1;
692
693 if (i>=len) return 0;
694 RdImgFormat=buf[i++]-'0';
695 if (RdImgFormat<1 || RdImgFormat>6) return -1;
696
697 r=ParseImageDecimal(buf+i,len-i,&RdImgW);
698 if (r<=0) return r;
699 if (RdImgW<1) return -1;
700 i+=r;
701
702 r=ParseImageDecimal(buf+i,len-i,&RdImgH);
703 if (r<=0) return r;
704 if (RdImgH<1) return -1;
705 i+=r;
706
707 if (RdImgFormat!=1 && RdImgFormat!=4) {
708 r=ParseImageDecimal(buf+i,len-i,&RdImgMaxVal);
709 if (r<=0) return r;
710 if (RdImgMaxVal<1 || RdImgMaxVal>65535) return -1;
711 i+=r;
712 }
713 else {
714 RdImgMaxVal=1;
715 }
716
717 if (i>=len) return 0;
718 if (buf[i++]!=0x0a) return -1;
719 return i;
720 }
721
722
ParseImageDecimal(const char * buf,int len,int * pNumber)723 int emPsRenderer::ParseImageDecimal(
724 const char * buf, int len, int * pNumber
725 )
726 {
727 int i,c,n;
728
729 for (i=0;;) {
730 if (i>=len) return 0;
731 c=(unsigned char)buf[i++];
732 if (c>='0' && c<='9') break;
733 if (c=='#') {
734 do {
735 if (i>=len) return 0;
736 c=(unsigned char)buf[i++];
737 } while (c!=0x0a && c!=0x0d);
738 }
739 else if (c>0x20) return -1;
740 }
741 n=c-'0';
742 for (;;) {
743 if (i>=len) return 0;
744 c=(unsigned char)buf[i++];
745 if (c>='0' && c<='9') {
746 n=n*10+(c-'0');
747 }
748 else {
749 *pNumber=n;
750 return i-1;
751 }
752 }
753 }
754
755
ParseImageData(const char * buf,int len)756 int emPsRenderer::ParseImageData(const char * buf, int len)
757 {
758 emImage * img;
759 int eat,w,d;
760 bool landscape;
761 emByte * p;
762 const emByte * s, * se;
763
764 if (RdImgFormat!=6 || RdImgMaxVal!=255) return -1;
765
766 if (CurrentJob) {
767 landscape=CurrentDocument.IsLandscapePage(CurrentPageIndex);
768 img=CurrentJob->Image;
769 if (img) {
770 if (landscape) {
771 if (img->GetWidth()!=RdImgH || img->GetHeight()!=RdImgW) {
772 return -1;
773 }
774 }
775 else {
776 if (img->GetWidth()!=RdImgW || img->GetHeight()!=RdImgH) {
777 return -1;
778 }
779 }
780 if (img->GetChannelCount()!=3) {
781 emFatalError("emPsRenderer: Output image must have 3 channels.");
782 }
783 }
784 }
785 else {
786 img=NULL;
787 landscape=false;
788 }
789
790 for (eat=0;;) {
791 w=(len-eat)/3;
792 if (w>RdImgW-RdImgX) w=RdImgW-RdImgX;
793 if (w<=0) break;
794 if (img) {
795 if (landscape) {
796 s=(const emByte*)buf+eat;
797 se=s+3*w;
798 p=img->GetWritableMap()+(RdImgX*RdImgH+RdImgH-1-RdImgY)*3;
799 d=RdImgH*3;
800 do {
801 p[0]=s[0];
802 p[1]=s[1];
803 p[2]=s[2];
804 p+=d;
805 s+=3;
806 } while (s<se);
807 }
808 else {
809 memcpy(
810 img->GetWritableMap()+(RdImgY*RdImgW+RdImgX)*3,
811 buf+eat,
812 w*3
813 );
814 }
815 }
816 eat+=w*3;
817 RdImgX+=w;
818 if (RdImgX>=RdImgW) {
819 RdImgX=0;
820 RdImgY++;
821 if (RdImgY>=RdImgH) {
822 RdImgDone=true;
823 break;
824 }
825 }
826 }
827 return eat;
828 }
829
830
PSAgentClass(emPsRenderer & interpreter)831 emPsRenderer::PSAgentClass::PSAgentClass(emPsRenderer & interpreter)
832 : emPriSchedAgent(interpreter.GetRootContext(),"cpu"),
833 Renderer(interpreter)
834
835 {
836 }
837
838
GotAccess()839 void emPsRenderer::PSAgentClass::GotAccess()
840 {
841 Renderer.WakeUp();
842 }
843
844
845 const char * const emPsRenderer::SyncString=
846 "SYNC823JVG73LS0GJ7B2TX2M49GZWK2D" // Just random
847 ;
848