finished implementing new bounding box logic
[swftools.git] / lib / pdf / BitmapOutputDev.cc
1 /* InfoOutputDev.h
2
3    Output Device which creates a bitmap.
4
5    Swftools is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    Swftools is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with swftools; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <memory.h>
22 #include <assert.h>
23 #include "config.h"
24 #include "BitmapOutputDev.h"
25 #include "GFXOutputDev.h"
26 #include "SplashBitmap.h"
27 #include "SplashPattern.h"
28 #include "Splash.h"
29 #include "../log.h"
30 #include "../png.h"
31 #include "../devices/record.h"
32 #include "../types.h"
33 #include "bbox.h"
34
35 #define UNKNOWN_BOUNDING_BOX 0,0,0,0
36
37 static SplashColor splash_white = {255,255,255};
38 static SplashColor splash_black = {0,0,0};
39     
40 ClipState::ClipState()
41 {
42     this->next = 0;
43     this->clipbitmap = 0;
44     this->written = 0;
45 }
46
47 BitmapOutputDev::BitmapOutputDev(InfoOutputDev*info, PDFDoc*doc)
48 {
49     this->info = info;
50     this->doc = doc;
51     this->xref = doc->getXRef();
52     
53     /* color graphic output device, for creating bitmaps */
54     this->rgbdev = new SplashOutputDev(splashModeRGB8, 1, gFalse, splash_white, gTrue, gTrue);
55   
56     /* color mode for binary bitmaps */
57     SplashColorMode colorMode = splashModeMono1;
58
59     /* two devices for testing things against clipping: one clips, the other doesn't */
60     this->clip0dev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
61     this->clip1dev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
62     
63     /* device indicating where polygonal pixels were drawn */
64     this->boolpolydev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
65     /* device indicating where text pixels were drawn */
66     this->booltextdev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
67
68     /* device for handling texts and links */
69     this->gfxdev = new GFXOutputDev(info, this->doc);
70
71     this->rgbdev->startDoc(this->xref);
72     this->boolpolydev->startDoc(this->xref);
73     this->booltextdev->startDoc(this->xref);
74     this->clip0dev->startDoc(this->xref);
75     this->clip1dev->startDoc(this->xref);
76
77     this->gfxoutput = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
78     gfxdevice_record_init(this->gfxoutput);
79
80     this->gfxdev->setDevice(this->gfxoutput);
81     
82     this->config_extrafontdata = 0;
83     this->bboxpath = 0;
84     //this->clipdev = 0;
85     //this->clipstates = 0;
86 }
87 BitmapOutputDev::~BitmapOutputDev()
88 {
89     if(this->gfxoutput) {
90         gfxresult_t*r = this->gfxoutput->finish(this->gfxoutput);
91         r->destroy(r);
92         free(this->gfxoutput);this->gfxoutput = 0;
93     }
94     if(this->bboxpath) {
95         delete this->bboxpath;this->bboxpath = 0;
96     }
97     if(this->rgbdev) {
98         delete this->rgbdev;this->rgbdev = 0;
99     }
100     if(this->gfxdev) {
101         delete this->gfxdev;this->gfxdev= 0;
102     }
103     if(this->boolpolydev) {
104         delete this->boolpolydev;this->boolpolydev = 0;
105     }
106     if(this->booltextdev) {
107         delete this->booltextdev;this->booltextdev = 0;
108     }
109     if(this->clip0dev) {
110         delete this->clip0dev;this->clip0dev = 0;
111     }
112     if(this->clip1dev) {
113         delete this->clip1dev;this->clip1dev = 0;
114     }
115     //if(this->clipbitmap) {
116     //    delete this->clipbitmap;this->clipbitmap = 0;
117     //}
118     //if(this->clipdev) {
119     //    delete this->clipdev;this->clipdev = 0;
120     //}
121
122 }
123
124 GBool BitmapOutputDev::getVectorAntialias()
125 {
126     return this->rgbdev->getVectorAntialias();
127 }
128 void BitmapOutputDev::setVectorAntialias(GBool vaa)
129 {
130     this->rgbdev->setVectorAntialias(vaa);
131 }
132 void BitmapOutputDev::setDevice(gfxdevice_t*dev)
133 {
134     this->dev = dev;
135 }
136 void BitmapOutputDev::setMove(int x,int y)
137 {
138     this->gfxdev->setMove(x,y);
139     this->user_movex = x;
140     this->user_movey = y;
141 }
142 void BitmapOutputDev::setClip(int x1,int y1,int x2,int y2)
143 {
144     this->gfxdev->setClip(x1,y1,x2,y2);
145     this->user_clipx1 = x1;
146     this->user_clipy1 = y1;
147     this->user_clipx2 = x2;
148     this->user_clipy2 = y2;
149 }
150 void BitmapOutputDev::setParameter(const char*key, const char*value)
151 {
152     if(!strcmp(key, "extrafontdata")) {
153         this->config_extrafontdata = atoi(value);
154     }
155     this->gfxdev->setParameter(key, value);
156 }
157 void BitmapOutputDev::setPageMap(int*page2page, int num_pages)
158 {
159     this->gfxdev->setPageMap(page2page, num_pages);
160 }
161
162 void BitmapOutputDev::flushBitmap()
163 {
164     int width = rgbdev->getBitmapWidth();
165     int height = rgbdev->getBitmapHeight();
166
167     if(sizeof(SplashColor)!=3) {
168         msg("<error> sizeof(SplashColor)!=3");
169         return;
170     }
171     
172     SplashColorPtr rgb = rgbbitmap->getDataPtr();
173     Guchar*alpha = rgbbitmap->getAlphaPtr();
174     Guchar*alpha2 = boolpolybitmap->getAlphaPtr();
175
176     ibbox_t* boxes = get_bitmap_bboxes((unsigned char*)alpha, width, height);
177     ibbox_t*b;
178
179     for(b=boxes;b;b=b->next) {
180         int xmin = b->xmin;
181         int ymin = b->ymin;
182         int xmax = b->xmax;
183         int ymax = b->ymax;
184
185         /* clip against (-movex, -movey, -movex+width, -movey+height) */
186         if(xmin < -this->movex) xmin = -this->movex;
187         if(ymin < -this->movey) ymin = -this->movey;
188         if(xmax > -this->movex + width) xmax = -this->movex+this->width;
189         if(ymax > -this->movey + height) ymax = -this->movey+this->height;
190
191         msg("<verbose> Flushing bitmap (bbox: %d,%d,%d,%d)", xmin,ymin,xmax,ymax);
192         
193         if((xmax-xmin)<=0 || (ymax-ymin)<=0) // no bitmap, nothing to do
194             continue;
195
196         int rangex = xmax-xmin;
197         int rangey = ymax-ymin;
198         gfximage_t*img = (gfximage_t*)malloc(sizeof(gfximage_t)); 
199         img->data = (gfxcolor_t*)malloc(rangex * rangey * 4);
200         img->width = rangex;
201         img->height = rangey;
202         int x,y;
203         for(y=0;y<rangey;y++) {
204             SplashColorPtr in=&rgb[((y+ymin)*width+xmin)*sizeof(SplashColor)];
205             gfxcolor_t*out = &img->data[y*rangex];
206             Guchar*ain = &alpha[(y+ymin)*width+xmin];
207             Guchar*ain2 = &alpha2[(y+ymin)*width+xmin];
208             if(this->emptypage) {
209                 for(x=0;x<rangex;x++) {
210                     /* the first bitmap on the page doesn't need to have an alpha channel-
211                        blend against a white background*/
212                     out[x].r = (in[x*3+0]*ain[x])/255 + 255-ain[x];
213                     out[x].g = (in[x*3+1]*ain[x])/255 + 255-ain[x];
214                     out[x].b = (in[x*3+2]*ain[x])/255 + 255-ain[x];
215                     out[x].a = 255;
216                 }
217             } else {
218                 for(x=0;x<rangex;x++) {
219                     /*
220                     if(!ain2[x]) {
221                         out[x].r = 0;out[x].g = 0;out[x].b = 0;out[x].a = 0;
222                     } else */
223
224                     /* according to endPage()/compositeBackground() in xpdf/SplashOutputDev.cc, we
225                        have to premultiply alpha (mix background and pixel according to the alpha channel).
226                     */
227                     out[x].r = (in[x*3+0]*ain[x])/255;
228                     out[x].g = (in[x*3+1]*ain[x])/255;
229                     out[x].b = (in[x*3+2]*ain[x])/255;
230                     out[x].a = ain[x];
231                 }
232             }
233         }
234
235         /* transform bitmap rectangle to "device space" */
236         xmin += movex;
237         ymin += movey;
238         xmax += movex;
239         ymax += movey;
240
241         gfxmatrix_t m;
242         m.tx = xmin;
243         m.ty = ymin;
244         m.m00 = m.m11 = 1;
245         m.m10 = m.m01 = 0;
246         m.tx -= 0.5;
247         m.ty -= 0.5;
248
249         gfxline_t* line = gfxline_makerectangle(xmin, ymin, xmax, ymax);
250         dev->fillbitmap(dev, line, img, &m, 0);
251         gfxline_free(line);
252     
253         free(img->data);img->data=0;free(img);img=0;
254     }
255     ibbox_destroy(boxes);
256
257     memset(rgbbitmap->getAlphaPtr(), 0, rgbbitmap->getWidth()*rgbbitmap->getHeight());
258     memset(rgbbitmap->getDataPtr(), 0, rgbbitmap->getRowSize()*rgbbitmap->getHeight());
259
260     this->emptypage = 0;
261 }
262
263 void BitmapOutputDev::flushText()
264 {
265     msg("<verbose> Flushing text/polygons");
266     gfxdevice_record_flush(this->gfxoutput, this->dev);
267     
268     this->emptypage = 0;
269 }
270
271 void writeMonoBitmap(SplashBitmap*btm, char*filename)
272 {
273     int width8 = (btm->getWidth()+7)/8;
274     int width = btm->getWidth();
275     int height = btm->getHeight();
276     gfxcolor_t*b = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
277     unsigned char*data = btm->getDataPtr();
278     int x,y;
279     for(y=0;y<height;y++) {
280         unsigned char*l = &data[width8*y];
281         gfxcolor_t*d = &b[width*y];
282         for(x=0;x<width;x++) {
283             if(l[x>>3]&(128>>(x&7))) {
284                 d[x].r = d[x].g = d[x].b = 255;
285             } else {
286                 d[x].r = d[x].g = d[x].b = 0;
287             }
288             d[x].a = 255;
289         }
290     }
291     writePNG(filename, (unsigned char*)b, width, height);
292     free(b);
293 }
294
295 void writeBitmap(SplashBitmap*bitmap, char*filename)
296 {
297     int y,x;
298     
299     int width = bitmap->getWidth();
300     int height = bitmap->getHeight();
301
302     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
303
304     if(bitmap->getMode()==splashModeMono1) {
305         writeMonoBitmap(bitmap, filename);
306         return;
307     }
308
309     for(y=0;y<height;y++) {
310         gfxcolor_t*line = &data[y*width];
311         for(x=0;x<width;x++) {
312             Guchar c[4] = {0,0,0,0};
313             bitmap->getPixel(x,y,c);
314             line[x].r = c[0];
315             line[x].g = c[1];
316             line[x].b = c[2];
317             line[x].a =  bitmap->getAlpha(x,y);
318         }
319     }
320     writePNG(filename, (unsigned char*)data, width, height);
321     free(data);
322 }
323
324 void writeAlpha(SplashBitmap*bitmap, char*filename)
325 {
326     int y,x;
327     
328     int width = bitmap->getWidth();
329     int height = bitmap->getHeight();
330     
331     if(bitmap->getMode()==splashModeMono1) {
332         writeMonoBitmap(bitmap, filename);
333         return;
334     }
335
336     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
337
338     for(y=0;y<height;y++) {
339         gfxcolor_t*line = &data[y*width];
340         for(x=0;x<width;x++) {
341             int a = bitmap->getAlpha(x,y);
342             line[x].r = a;
343             line[x].g = a;
344             line[x].b = a;
345             line[x].a = 255;
346         }
347     }
348     writePNG(filename, (unsigned char*)data, width, height);
349     free(data);
350 }
351 static int dbg_btm_counter=1;
352
353 static const char*STATE_NAME[] = {"parallel", "textabovebitmap", "bitmapabovetext"};
354
355 int checkAlphaSanity(SplashBitmap*boolbtm, SplashBitmap*alphabtm)
356 {
357     assert(boolbtm->getWidth() == alphabtm->getWidth());
358     assert(boolbtm->getHeight() == alphabtm->getHeight());
359     if(boolbtm->getMode()==splashModeMono1) {
360         return 1;
361     }
362
363     int width = boolbtm->getWidth();
364     int height = boolbtm->getHeight();
365
366     int bad=0;
367     int x,y;
368     for(y=0;y<height;y++) {
369         for(x=0;x<width;x++) {
370             int a1 = alphabtm->getAlpha(x,y);
371             int a2 = boolbtm->getAlpha(x,y);
372             if(a1!=a2) {
373                 bad++;
374             }
375         }
376     }
377     double badness = bad/(double)(width*height);
378     if(badness>0.2) {
379         msg("<error> Bitmaps don't correspond: %d out of %d pixels wrong (%.2f%%)", bad, width*height, 
380                 badness*100.0);
381         return 0;
382     }
383     msg("<notice> %f", badness);
384     return 1;
385 }
386
387 GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2)
388 {
389     /* called once some new text was drawn on booltextdev, and
390        before the same thing is drawn on gfxdev */
391    
392     msg("<trace> Testing new text data against current bitmap data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
393     
394     if(0) {
395         char filename1[80];
396         char filename2[80];
397         char filename3[80];
398         sprintf(filename1, "state%dboolbitmap_afternewtext.png", dbg_btm_counter);
399         sprintf(filename2, "state%dbooltext_afternewtext.png", dbg_btm_counter);
400         sprintf(filename3, "state%dbitmap_afternewtext.png", dbg_btm_counter);
401         msg("<verbose> %s %s %s", filename1, filename2, filename3);
402         writeAlpha(boolpolybitmap, filename1);
403         writeAlpha(booltextbitmap, filename2);
404         writeBitmap(rgbdev->getBitmap(), filename3);
405     }
406     dbg_btm_counter++;
407
408     GBool ret = false;
409     if(intersection(x1,y1,x2,y2)) {
410         if(layerstate==STATE_PARALLEL) {
411             /* the new text is above the bitmap. So record that fact,
412                and also clear the bitmap buffer, so we can check for
413                new intersections */
414             msg("<verbose> Text is above current bitmap/polygon data");
415             layerstate=STATE_TEXT_IS_ABOVE;
416             clearBoolPolyDev(x1,y1,x2,y2);
417         } else if(layerstate==STATE_BITMAP_IS_ABOVE) {
418             /* there's a bitmap above the (old) text. So we need
419                to flush out that text, and record that the *new*
420                text is now *above* the bitmap
421              */
422             msg("<verbose> Text is above current bitmap/polygon data (which is above some other text)");
423             flushText();
424             layerstate=STATE_TEXT_IS_ABOVE;
425             /* clear both bool devices- the text device because
426                we just dumped out all the (old) text, and the
427                poly dev so we can check for new intersections */
428             clearBoolPolyDev(x1,y1,x2,y2);
429             /* FIXME this destroys the text pixels we just
430                drew to test for the intersection- however we need
431                those to check for the *new* intersections */
432             clearBoolTextDev(x1,y1,x2,y2);
433             ret = true;
434         } else {
435             /* we already know that the current text section is
436                above the current bitmap section- now just new
437                bitmap data *and* new text data was drawn, and
438                *again* it's above the current bitmap- so clear
439                the polygon bitmap again, so we can check for
440                new intersections */
441             msg("<verbose> Text is still above current bitmap/polygon data");
442             clearBoolPolyDev(x1,y1,x2,y2);
443         }
444     } 
445     return ret;
446 }
447
448 GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2)
449 {
450     /* similar to checkNewText() above, only in reverse */
451     msg("<trace> Testing new graphics data against current text data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
452
453     if(0) {
454         char filename1[80];
455         char filename2[80];
456         char filename3[80];
457         sprintf(filename1, "state%dboolbitmap_afternewgfx.png", dbg_btm_counter);
458         sprintf(filename2, "state%dbooltext_afternewgfx.png", dbg_btm_counter);
459         sprintf(filename3, "state%dbitmap_afternewgfx.png", dbg_btm_counter);
460         msg("<verbose> %s %s %s", filename1, filename2, filename3);
461         writeAlpha(boolpolybitmap, filename1);
462         writeAlpha(booltextbitmap, filename2);
463         writeBitmap(rgbdev->getBitmap(), filename3);
464     }
465     dbg_btm_counter++;
466
467     GBool ret = false;
468     if(intersection(x1,y1,x2,y2)) {
469         if(layerstate==STATE_PARALLEL) {
470             msg("<verbose> Bitmap is above current text data");
471             layerstate=STATE_BITMAP_IS_ABOVE;
472             clearBoolTextDev(x1,y1,x2,y2);
473         } else if(layerstate==STATE_TEXT_IS_ABOVE) {
474             msg("<verbose> Bitmap is above current text data (which is above some bitmap)");
475             flushBitmap();
476             layerstate=STATE_BITMAP_IS_ABOVE;
477             clearBoolTextDev(x1,y1,x2,y2);
478             /* FIXME this destroys the polygon pixels we just
479                drew to test for the intersection- however we need
480                those to check for the *new* intersections */
481             clearBoolPolyDev(x1,y1,x2,y2);
482             ret = true;
483         } else {
484             msg("<verbose> Bitmap is still above current text data");
485             clearBoolTextDev(x1,y1,x2,y2);
486         }
487     } 
488     return ret;
489 }
490
491 //void checkNewText() {
492 //    Guchar*alpha = rgbbitmap->getAlphaPtr();
493 //    Guchar*charpixels = clip1bitmap->getDataPtr();
494 //    int xx,yy;
495 //    for(yy=0;yy<height;yy++) {
496 //        Guchar*aline = &alpha[yy*width];
497 //        Guchar*cline = &charpixels[yy*width8];
498 //        for(xx=0;xx<width;xx++) {
499 //            int bit = xx&7;
500 //            int bytepos = xx>>3;
501 //            /* TODO: is the bit order correct? */
502 //            if(aline[xx] && (cline[bytepos]&(1<<bit))) 
503 //              break;
504 //        }
505 //        if(xx!=width)
506 //            break;
507 //}
508
509 static inline GBool fixBBox(int*x1, int*y1, int*x2, int*y2, int width, int height)
510 {
511     if(!(*x1|*y1|*x2|*y2)) {
512         // undefined bbox
513         *x1 = *y1 = 0;
514         *x2 = width;
515         *y2 = height;
516         return gTrue;
517     }
518     if(*x2<=*x1) return gFalse;
519     if(*x2<0) return gFalse;
520     if(*x1<0) *x1 = 0;
521     if(*x1>=width) return gFalse;
522     if(*x2>width) *x2=width;
523
524     if(*y2<=*y1) return gFalse;
525     if(*y2<0) return gFalse;
526     if(*y1<0) *y1 = 0;
527     if(*y1>=height) return gFalse;
528     if(*y2>height) *y2=height;
529     return gTrue;
530 }
531
532 GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2)
533 {
534     if(clip0bitmap->getMode()==splashModeMono1) {
535         int width = clip0bitmap->getWidth();
536         int width8 = (width+7)/8;
537         int height = clip0bitmap->getHeight();
538
539         if(!fixBBox(&x1,&y1,&x2,&y2,width,height)) {
540             /* area is outside or null */
541             return gFalse;
542         }
543         
544         SplashBitmap*clip0 = clip0bitmap;
545         SplashBitmap*clip1 = clip1bitmap;
546         int x18 = x1/8;
547         int x28 = (x2+7)/8;
548         int y;
549
550         for(y=y1;y<y2;y++) {
551             unsigned char*row1 = &clip0bitmap->getDataPtr()[width8*y+x18];
552             unsigned char*row2 = &clip1bitmap->getDataPtr()[width8*y+x18];
553             if(memcmp(row1, row2, x28-x18)) {
554                 return gTrue;
555             }
556         }
557         return gFalse;
558     } else {
559         SplashBitmap*clip0 = clip0bitmap;
560         SplashBitmap*clip1 = clip1bitmap;
561         int width = clip0->getAlphaRowSize();
562         int height = clip0->getHeight();
563
564         if(!fixBBox(&x1, &y1, &x2, &y2, width, height)) {
565             x1=y1=0;x2=y2=1;
566         }
567
568         Guchar*a0 = clip0->getAlphaPtr();
569         Guchar*a1 = clip1->getAlphaPtr();
570         int x,y;
571         char differs=0;
572         for(y=y1;y<y2;y++) {
573             for(x=x1;x<x2;x++) {
574                 if(a0[y*width+x]!=a1[y*width+x]) {
575                     differs=1;
576                     break;
577                 }
578             }
579             if(differs)
580                 break;
581         }
582         char differs2 = memcmp(a0, a1, width*height);
583         if(differs && !differs2) 
584             msg("<warning> Strange internal error (2)");
585         else if(!differs && differs2) {
586             msg("<warning> Bad Bounding Box: Difference in clip0 and clip1 outside bbox");
587             msg("<warning> %d %d %d %d", x1, y1, x2, y2);
588         }
589         return differs2;
590     }
591 }
592
593 static void clearBooleanBitmap(SplashBitmap*btm, int x1, int y1, int x2, int y2)
594 {
595     if(!(x1|y1|x2|y2)) {
596         x1 = y1 = 0;
597         x2 = btm->getWidth();
598         y2 = btm->getHeight();
599     }
600     if(btm->getMode()==splashModeMono1) {
601         int width8 = (btm->getWidth()+7)/8;
602         int width = btm->getWidth();
603         int height = btm->getHeight();
604         memset(btm->getDataPtr(), 0, width8*height);
605     } else {
606         int width = btm->getAlphaRowSize();
607         int height = btm->getHeight();
608         memset(btm->getAlphaPtr(), 0, width*height);
609     }
610 }
611
612 GBool compare8(unsigned char*data1, unsigned char*data2, int len)
613 {
614     if(!len)
615         return 0;
616     if(((ptroff_t)data1&7)==((ptroff_t)data2&7)) {
617         // oh good, we can do aligning
618         while((ptroff_t)data1&7) {
619             if(*data1&*data2)
620                 return 1;
621             data1++;
622             data2++;
623             if(!--len)
624                 return 0;
625         }
626     }
627     /* use 64 bit for the (hopefully aligned) middle section */
628     int l8 = len/8;
629     long long unsigned int*d1 = (long long unsigned int*)data1;
630     long long unsigned int*d2 = (long long unsigned int*)data2;
631     long long unsigned int x = 0;
632     int t;
633     for(t=0;t<l8;t++) {
634         x |= d1[t]&d2[t];
635     }
636     if(x)
637         return 1;
638
639     data1+=l8*8;
640     data2+=l8*8;
641     len -= l8*8;
642     for(t=0;t<len;t++) {
643         if(data1[t]&data2[t]) {
644             return 1;
645         }
646     }
647     return 0;
648 }
649
650 GBool BitmapOutputDev::intersection(int x1, int y1, int x2, int y2)
651 {
652     SplashBitmap*boolpoly = boolpolybitmap;
653     SplashBitmap*booltext = booltextbitmap;
654             
655     if(boolpoly->getMode()==splashModeMono1) {
656         /* alternative implementation, using one bit per pixel-
657            needs the no-dither patch in xpdf */
658         
659         int width = boolpoly->getWidth();
660         int height = boolpoly->getHeight();
661
662         if(!fixBBox(&x1,&y1,&x2,&y2, width, height)) {
663             return gFalse;
664         }
665
666         Guchar*polypixels = boolpoly->getDataPtr();
667         Guchar*textpixels = booltext->getDataPtr();
668
669         int width8 = (width+7)/8;
670         int runx = width8;
671         int runy = height;
672         
673         if(x1|y1|x2|y2) {
674             polypixels+=y1*width8+x1/8;
675             textpixels+=y1*width8+x1/8;
676             runx=(x2+7)/8 - x1/8;
677             runy=y2-y1;
678         }
679         
680         int t;
681         unsigned char c=0;
682         
683         /*assert(sizeof(unsigned long long int)==8);
684         if(((ptroff_t)polypixels&7) || ((ptroff_t)textpixels&7)) {
685             //msg("<warning> Non-optimal alignment");
686         }*/
687
688         int x,y;
689         unsigned char*data1 = (unsigned char*)polypixels;
690         unsigned char*data2 = (unsigned char*)textpixels;
691         msg("<verbose> Testing area (%d,%d,%d,%d), runx=%d,runy=%d", x1,y1,x2,y2, runx, runy);
692         for(y=0;y<runy;y++) {
693             if(compare8(data1,data2,runx))
694                 return gTrue;
695             data1+=width8;
696             data2+=width8;
697         }
698         return gFalse;
699     } else {
700         int width = boolpoly->getAlphaRowSize();
701         int height = boolpoly->getHeight();
702
703         if(!fixBBox(&x1, &y1, &x2, &y2, width, height)) {
704             x1=y1=0;x2=y2=1;
705         }
706         Guchar*polypixels = boolpoly->getAlphaPtr();
707         Guchar*textpixels = booltext->getAlphaPtr();
708
709         int x,y;
710         char overlap1 = 0;
711         char overlap2 = 0;
712         for(x=x1;x<x2;x++) {
713             for(y=y1;y<y2;y++) {
714                 if(polypixels[width*y+x]&&textpixels[width*y+x])
715                     overlap1 = 1;
716             }
717         }
718         int ax1=0,ay1=0,ax2=0,ay2=0;
719         for(y=0;y<height;y++) {
720             for(x=0;x<width;x++) {
721                 if(polypixels[width*y+x]&&textpixels[width*y+x]) {
722                     overlap2 = 1;
723                     if(!(ax1|ay1|ax2|ay2)) {
724                         ax1 = ax2 = x;
725                         ay1 = ay2 = y;
726                     } else {
727                         ax1 = ax1<x?ax1:x;
728                         ay1 = ay1<y?ay1:y;
729                         ax2 = ax2>x?ax2:x;
730                         ay2 = ay2>y?ay2:y;
731                     }
732                 }
733             }
734         }
735         if(overlap1 && !overlap2)
736             msg("<warning> strange internal error");
737         if(!overlap1 && overlap2) {
738             msg("<warning> Bad bounding box: intersection outside bbox");
739             msg("<warning> given bbox: %d %d %d %d", x1, y1, x2, y2);
740             msg("<warning> changed area: %d %d %d %d", ax1, ay1, ax2, ay2);
741             //writeAlpha(booltextbitmap, "alpha.png");
742         }
743         return overlap2;
744     }
745 }
746
747
748 void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
749 {
750     double x1,y1,x2,y2;
751     state->transform(crop_x1,crop_y1,&x1,&y1);
752     state->transform(crop_x2,crop_y2,&x2,&y2);
753     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
754     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
755     
756     this->movex = -(int)x1 - user_movex;
757     this->movey = -(int)y1 - user_movey;
758     
759     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
760         x1 = user_clipx1;
761         x2 = user_clipx2;
762         y1 = user_clipy1;
763         y2 = user_clipy2;
764     }
765     this->width = (int)(x2-x1);
766     this->height = (int)(y2-y1);
767
768     msg("<debug> startPage");
769     rgbdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
770     boolpolydev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
771     booltextdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
772     clip0dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
773     clip1dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
774     gfxdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
775
776     boolpolybitmap = boolpolydev->getBitmap();
777     booltextbitmap = booltextdev->getBitmap();
778     clip0bitmap = clip0dev->getBitmap();
779     clip1bitmap = clip1dev->getBitmap();
780     rgbbitmap = rgbdev->getBitmap();
781     
782     flushText(); // write out the initial clipping rectangle
783
784     /* just in case any device did draw a white background rectangle 
785        into the device */
786     clearBoolTextDev(UNKNOWN_BOUNDING_BOX);
787     clearBoolPolyDev(UNKNOWN_BOUNDING_BOX);
788
789     this->layerstate = STATE_PARALLEL;
790     this->emptypage = 1;
791     msg("<debug> startPage done");
792 }
793
794 void BitmapOutputDev::endPage()
795 {
796     msg("<verbose> endPage (BitmapOutputDev)");
797
798     /* notice: we're not fully done yet with this page- there might still be 
799        a few calls to drawLink() yet to come */
800 }
801 void BitmapOutputDev::finishPage()
802 {
803     msg("<verbose> finishPage (BitmapOutputDev)");
804     gfxdev->endPage();
805    
806     if(layerstate == STATE_BITMAP_IS_ABOVE) {
807         this->flushText();
808         this->flushBitmap();
809     } else {
810         this->flushBitmap();
811         this->flushText();
812     }
813
814     /* splash will now destroy alpha, and paint the 
815        background color into the "holes" in the bitmap */
816     boolpolydev->endPage();
817     booltextdev->endPage();
818     rgbdev->endPage();
819     clip0dev->endPage();
820     clip1dev->endPage();
821 }
822
823 GBool BitmapOutputDev::upsideDown()
824 {
825     boolpolydev->upsideDown();
826     booltextdev->upsideDown();
827     clip0dev->upsideDown();
828     clip1dev->upsideDown();
829     return rgbdev->upsideDown();
830 }
831
832 GBool BitmapOutputDev::useDrawChar()
833 {
834     boolpolydev->useDrawChar();
835     booltextdev->useDrawChar();
836     clip0dev->useDrawChar();
837     clip1dev->useDrawChar();
838     return rgbdev->useDrawChar();
839 }
840
841 GBool BitmapOutputDev::useTilingPatternFill()
842 {
843     boolpolydev->useTilingPatternFill();
844     booltextdev->useTilingPatternFill();
845     clip0dev->useTilingPatternFill();
846     clip1dev->useTilingPatternFill();
847     return rgbdev->useTilingPatternFill();
848 }
849
850 GBool BitmapOutputDev::useShadedFills()
851 {
852     boolpolydev->useShadedFills();
853     booltextdev->useShadedFills();
854     clip0dev->useShadedFills();
855     clip1dev->useShadedFills();
856     return rgbdev->useShadedFills();
857 }
858
859 GBool BitmapOutputDev::useDrawForm()
860 {
861     boolpolydev->useDrawForm();
862     booltextdev->useDrawForm();
863     clip0dev->useDrawForm();
864     clip1dev->useDrawForm();
865     return rgbdev->useDrawForm();
866 }
867
868 GBool BitmapOutputDev::interpretType3Chars()
869 {
870     boolpolydev->interpretType3Chars();
871     booltextdev->interpretType3Chars();
872     clip0dev->interpretType3Chars();
873     clip1dev->interpretType3Chars();
874     return rgbdev->interpretType3Chars();
875 }
876
877 GBool BitmapOutputDev::needNonText() 
878 {
879     boolpolydev->needNonText();
880     booltextdev->needNonText();
881     clip0dev->needNonText();
882     clip1dev->needNonText();
883     return rgbdev->needNonText();
884 }
885 /*GBool BitmapOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
886                            int rotate, GBool useMediaBox, GBool crop,
887                            int sliceX, int sliceY, int sliceW, int sliceH,
888                            GBool printing, Catalog *catalog,
889                            GBool (*abortCheckCbk)(void *data),
890                            void *abortCheckCbkData)
891 {
892     return gTrue;
893 }*/
894 void BitmapOutputDev::setDefaultCTM(double *ctm) 
895 {
896     boolpolydev->setDefaultCTM(ctm);
897     booltextdev->setDefaultCTM(ctm);
898     rgbdev->setDefaultCTM(ctm);
899     clip0dev->setDefaultCTM(ctm);
900     clip1dev->setDefaultCTM(ctm);
901     gfxdev->setDefaultCTM(ctm);
902 }
903 void BitmapOutputDev::saveState(GfxState *state) 
904 {
905     boolpolydev->saveState(state);
906     booltextdev->saveState(state);
907     rgbdev->saveState(state);
908     clip0dev->saveState(state);
909     clip1dev->saveState(state);
910
911     /*ClipState*cstate = new ClipState();
912     cstate->next = this->clipstates;
913     this->clipstates = cstate;*/
914 }
915 void BitmapOutputDev::restoreState(GfxState *state) 
916 {
917     boolpolydev->restoreState(state);
918     booltextdev->restoreState(state);
919     rgbdev->restoreState(state);
920     clip0dev->restoreState(state);
921     clip1dev->restoreState(state);
922
923     /*if(this->clipstates) {
924         ClipState*old = this->clipstates;
925         if(old->written) {
926             gfxdev->restoreState(state);
927         }
928         this->clipstates = this->clipstates->next;
929         delete(old);
930     } else {
931         msg("<error> invalid restoreState()");
932     }*/
933 }
934 void BitmapOutputDev::updateAll(GfxState *state)
935 {
936     boolpolydev->updateAll(state);
937     booltextdev->updateAll(state);
938     rgbdev->updateAll(state);
939     clip0dev->updateAll(state);
940     clip1dev->updateAll(state);
941     gfxdev->updateAll(state);
942 }
943 void BitmapOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
944 {
945     boolpolydev->updateCTM(state,m11,m12,m21,m22,m31,m32);
946     booltextdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
947     rgbdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
948     clip0dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
949     clip1dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
950     gfxdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
951 }
952 void BitmapOutputDev::updateLineDash(GfxState *state)
953 {
954     boolpolydev->updateLineDash(state);
955     booltextdev->updateLineDash(state);
956     rgbdev->updateLineDash(state);
957     clip0dev->updateLineDash(state);
958     clip1dev->updateLineDash(state);
959     gfxdev->updateLineDash(state);
960 }
961 void BitmapOutputDev::updateFlatness(GfxState *state)
962 {
963     boolpolydev->updateFlatness(state);
964     booltextdev->updateFlatness(state);
965     rgbdev->updateFlatness(state);
966     clip0dev->updateFlatness(state);
967     clip1dev->updateFlatness(state);
968     gfxdev->updateFlatness(state);
969 }
970 void BitmapOutputDev::updateLineJoin(GfxState *state)
971 {
972     boolpolydev->updateLineJoin(state);
973     booltextdev->updateLineJoin(state);
974     rgbdev->updateLineJoin(state);
975     clip0dev->updateLineJoin(state);
976     clip1dev->updateLineJoin(state);
977     gfxdev->updateLineJoin(state);
978 }
979 void BitmapOutputDev::updateLineCap(GfxState *state)
980 {
981     boolpolydev->updateLineCap(state);
982     booltextdev->updateLineCap(state);
983     rgbdev->updateLineCap(state);
984     clip0dev->updateLineCap(state);
985     clip1dev->updateLineCap(state);
986     gfxdev->updateLineCap(state);
987 }
988 void BitmapOutputDev::updateMiterLimit(GfxState *state)
989 {
990     boolpolydev->updateMiterLimit(state);
991     booltextdev->updateMiterLimit(state);
992     rgbdev->updateMiterLimit(state);
993     clip0dev->updateMiterLimit(state);
994     clip1dev->updateMiterLimit(state);
995     gfxdev->updateMiterLimit(state);
996 }
997 void BitmapOutputDev::updateLineWidth(GfxState *state)
998 {
999     boolpolydev->updateLineWidth(state);
1000     booltextdev->updateLineWidth(state);
1001     rgbdev->updateLineWidth(state);
1002     clip0dev->updateLineWidth(state);
1003     clip1dev->updateLineWidth(state);
1004     gfxdev->updateLineWidth(state);
1005 }
1006 void BitmapOutputDev::updateStrokeAdjust(GfxState *state)
1007 {
1008     boolpolydev->updateStrokeAdjust(state);
1009     booltextdev->updateStrokeAdjust(state);
1010     rgbdev->updateStrokeAdjust(state);
1011     clip0dev->updateStrokeAdjust(state);
1012     clip1dev->updateStrokeAdjust(state);
1013     gfxdev->updateStrokeAdjust(state);
1014 }
1015 void BitmapOutputDev::updateFillColorSpace(GfxState *state)
1016 {
1017     boolpolydev->updateFillColorSpace(state);
1018     booltextdev->updateFillColorSpace(state);
1019     rgbdev->updateFillColorSpace(state);
1020     clip0dev->updateFillColorSpace(state);
1021     clip1dev->updateFillColorSpace(state);
1022     gfxdev->updateFillColorSpace(state);
1023 }
1024 void BitmapOutputDev::updateStrokeColorSpace(GfxState *state)
1025 {
1026     boolpolydev->updateStrokeColorSpace(state);
1027     booltextdev->updateStrokeColorSpace(state);
1028     rgbdev->updateStrokeColorSpace(state);
1029     clip0dev->updateStrokeColorSpace(state);
1030     clip1dev->updateStrokeColorSpace(state);
1031     gfxdev->updateStrokeColorSpace(state);
1032 }
1033 void BitmapOutputDev::updateFillColor(GfxState *state)
1034 {
1035     boolpolydev->updateFillColor(state);
1036     booltextdev->updateFillColor(state);
1037     rgbdev->updateFillColor(state);
1038     clip0dev->updateFillColor(state);
1039     clip1dev->updateFillColor(state);
1040     gfxdev->updateFillColor(state);
1041 }
1042 void BitmapOutputDev::updateStrokeColor(GfxState *state)
1043 {
1044     boolpolydev->updateStrokeColor(state);
1045     booltextdev->updateStrokeColor(state);
1046     rgbdev->updateStrokeColor(state);
1047     clip0dev->updateStrokeColor(state);
1048     clip1dev->updateStrokeColor(state);
1049     gfxdev->updateStrokeColor(state);
1050 }
1051 void BitmapOutputDev::updateBlendMode(GfxState *state)
1052 {
1053     boolpolydev->updateBlendMode(state);
1054     booltextdev->updateBlendMode(state);
1055     rgbdev->updateBlendMode(state);
1056     clip0dev->updateBlendMode(state);
1057     clip1dev->updateBlendMode(state);
1058     gfxdev->updateBlendMode(state);
1059 }
1060 void BitmapOutputDev::updateFillOpacity(GfxState *state)
1061 {
1062     boolpolydev->updateFillOpacity(state);
1063     booltextdev->updateFillOpacity(state);
1064     rgbdev->updateFillOpacity(state);
1065     clip0dev->updateFillOpacity(state);
1066     clip1dev->updateFillOpacity(state);
1067     gfxdev->updateFillOpacity(state);
1068 }
1069 void BitmapOutputDev::updateStrokeOpacity(GfxState *state)
1070 {
1071     boolpolydev->updateStrokeOpacity(state);
1072     booltextdev->updateStrokeOpacity(state);
1073     rgbdev->updateStrokeOpacity(state);
1074     clip0dev->updateStrokeOpacity(state);
1075     clip1dev->updateStrokeOpacity(state);
1076     gfxdev->updateStrokeOpacity(state);
1077 }
1078 void BitmapOutputDev::updateFillOverprint(GfxState *state)
1079 {
1080     boolpolydev->updateFillOverprint(state);
1081     booltextdev->updateFillOverprint(state);
1082     rgbdev->updateFillOverprint(state);
1083     clip0dev->updateFillOverprint(state);
1084     clip1dev->updateFillOverprint(state);
1085     gfxdev->updateFillOverprint(state);
1086 }
1087 void BitmapOutputDev::updateStrokeOverprint(GfxState *state)
1088 {
1089     boolpolydev->updateStrokeOverprint(state);
1090     booltextdev->updateStrokeOverprint(state);
1091     rgbdev->updateStrokeOverprint(state);
1092     clip0dev->updateStrokeOverprint(state);
1093     clip1dev->updateStrokeOverprint(state);
1094     gfxdev->updateStrokeOverprint(state);
1095 }
1096 void BitmapOutputDev::updateTransfer(GfxState *state)
1097 {
1098     boolpolydev->updateTransfer(state);
1099     booltextdev->updateTransfer(state);
1100     rgbdev->updateTransfer(state);
1101     clip0dev->updateTransfer(state);
1102     clip1dev->updateTransfer(state);
1103     gfxdev->updateTransfer(state);
1104 }
1105
1106 void BitmapOutputDev::updateFont(GfxState *state)
1107 {
1108     boolpolydev->updateFont(state);
1109     booltextdev->updateFont(state);
1110     rgbdev->updateFont(state);
1111     clip0dev->updateFont(state);
1112     clip1dev->updateFont(state);
1113     gfxdev->updateFont(state);
1114 }
1115 void BitmapOutputDev::updateTextMat(GfxState *state)
1116 {
1117     boolpolydev->updateTextMat(state);
1118     booltextdev->updateTextMat(state);
1119     rgbdev->updateTextMat(state);
1120     clip0dev->updateTextMat(state);
1121     clip1dev->updateTextMat(state);
1122     gfxdev->updateTextMat(state);
1123 }
1124 void BitmapOutputDev::updateCharSpace(GfxState *state)
1125 {
1126     boolpolydev->updateCharSpace(state);
1127     booltextdev->updateCharSpace(state);
1128     rgbdev->updateCharSpace(state);
1129     clip0dev->updateCharSpace(state);
1130     clip1dev->updateCharSpace(state);
1131     gfxdev->updateCharSpace(state);
1132 }
1133 void BitmapOutputDev::updateRender(GfxState *state)
1134 {
1135     boolpolydev->updateRender(state);
1136     booltextdev->updateRender(state);
1137     rgbdev->updateRender(state);
1138     clip0dev->updateRender(state);
1139     clip1dev->updateRender(state);
1140     gfxdev->updateRender(state);
1141 }
1142 void BitmapOutputDev::updateRise(GfxState *state)
1143 {
1144     boolpolydev->updateRise(state);
1145     booltextdev->updateRise(state);
1146     rgbdev->updateRise(state);
1147     clip0dev->updateRise(state);
1148     clip1dev->updateRise(state);
1149     gfxdev->updateRise(state);
1150 }
1151 void BitmapOutputDev::updateWordSpace(GfxState *state)
1152 {
1153     boolpolydev->updateWordSpace(state);
1154     booltextdev->updateWordSpace(state);
1155     rgbdev->updateWordSpace(state);
1156     clip0dev->updateWordSpace(state);
1157     clip1dev->updateWordSpace(state);
1158     gfxdev->updateWordSpace(state);
1159 }
1160 void BitmapOutputDev::updateHorizScaling(GfxState *state)
1161 {
1162     boolpolydev->updateHorizScaling(state);
1163     booltextdev->updateHorizScaling(state);
1164     rgbdev->updateHorizScaling(state);
1165     clip0dev->updateHorizScaling(state);
1166     clip1dev->updateHorizScaling(state);
1167     gfxdev->updateHorizScaling(state);
1168 }
1169 void BitmapOutputDev::updateTextPos(GfxState *state)
1170 {
1171     boolpolydev->updateTextPos(state);
1172     booltextdev->updateTextPos(state);
1173     rgbdev->updateTextPos(state);
1174     clip0dev->updateTextPos(state);
1175     clip1dev->updateTextPos(state);
1176     gfxdev->updateTextPos(state);
1177 }
1178 void BitmapOutputDev::updateTextShift(GfxState *state, double shift)
1179 {
1180     boolpolydev->updateTextShift(state, shift);
1181     booltextdev->updateTextShift(state, shift);
1182     rgbdev->updateTextShift(state, shift);
1183     clip0dev->updateTextShift(state, shift);
1184     clip1dev->updateTextShift(state, shift);
1185     gfxdev->updateTextShift(state, shift);
1186 }
1187
1188 double max(double x, double y) 
1189 {
1190     return x>y?x:y;
1191 }
1192 double min(double x, double y) 
1193 {
1194     return x<y?x:y;
1195 }
1196
1197 gfxbbox_t BitmapOutputDev::getBBox(GfxState*state)
1198 {
1199     GfxPath * path = state->getPath();
1200     int num = path->getNumSubpaths();
1201     gfxbbox_t bbox = {0,0,1,1};
1202     char valid=0;
1203     int t;
1204     for(t = 0; t < num; t++) {
1205         GfxSubpath *subpath = path->getSubpath(t);
1206         int subnum = subpath->getNumPoints();
1207         int s;
1208         for(s=0;s<subnum;s++) {
1209            double x,y;
1210            state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
1211            if(!valid) {
1212                bbox.xmin = x; bbox.ymin = y;
1213                bbox.xmax = x; bbox.ymax = y;
1214                valid = 1;
1215            } else {
1216                bbox.xmin = min(bbox.xmin, x);
1217                bbox.ymin = min(bbox.ymin, y);
1218                bbox.xmax = max(bbox.xmax, x);
1219                bbox.ymax = max(bbox.ymax, y);
1220            }
1221         }
1222     }
1223     return bbox;
1224 }
1225
1226 void BitmapOutputDev::stroke(GfxState *state)
1227 {
1228     msg("<debug> stroke");
1229     boolpolydev->stroke(state);
1230     gfxbbox_t bbox = getBBox(state);
1231     double width = state->getTransformedLineWidth();
1232     bbox.xmin -= width; bbox.ymin -= width;
1233     bbox.xmax += width; bbox.ymax += width;
1234     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1235     rgbdev->stroke(state);
1236 }
1237 void BitmapOutputDev::fill(GfxState *state)
1238 {
1239     msg("<debug> fill");
1240     boolpolydev->fill(state);
1241     gfxbbox_t bbox = getBBox(state);
1242     if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) {
1243         boolpolydev->fill(state);
1244     }
1245     rgbdev->fill(state);
1246 }
1247 void BitmapOutputDev::eoFill(GfxState *state)
1248 {
1249     msg("<debug> eoFill");
1250     boolpolydev->eoFill(state);
1251     gfxbbox_t bbox = getBBox(state);
1252     if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) {
1253         boolpolydev->eoFill(state);
1254     }
1255     rgbdev->eoFill(state);
1256 }
1257 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1258 void BitmapOutputDev::tilingPatternFill(GfxState *state, Object *str,
1259                                int paintType, Dict *resDict,
1260                                double *mat, double *bbox,
1261                                int x0, int y0, int x1, int y1,
1262                                double xStep, double yStep)
1263 {
1264     msg("<debug> tilingPatternFill");
1265     boolpolydev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1266     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1267     rgbdev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1268 }
1269 #else
1270 void BitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
1271                                int paintType, Dict *resDict,
1272                                double *mat, double *bbox,
1273                                int x0, int y0, int x1, int y1,
1274                                double xStep, double yStep) 
1275 {
1276     msg("<debug> tilingPatternFill");
1277     boolpolydev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1278     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1279     rgbdev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1280 }
1281 #endif
1282
1283 GBool BitmapOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
1284 {
1285     msg("<debug> functionShadedFill");
1286     boolpolydev->functionShadedFill(state, shading);
1287     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1288     return rgbdev->functionShadedFill(state, shading);
1289 }
1290 GBool BitmapOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1291 {
1292     msg("<debug> axialShadedFill");
1293     boolpolydev->axialShadedFill(state, shading);
1294     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1295     return rgbdev->axialShadedFill(state, shading);
1296 }
1297 GBool BitmapOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1298 {
1299     msg("<debug> radialShadedFill");
1300     boolpolydev->radialShadedFill(state, shading);
1301     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1302     return rgbdev->radialShadedFill(state, shading);
1303 }
1304
1305 SplashColor black = {0,0,0};
1306 SplashColor white = {255,255,255};
1307
1308 void BitmapOutputDev::clip(GfxState *state)
1309 {
1310     msg("<debug> clip");
1311     boolpolydev->clip(state);
1312     booltextdev->clip(state);
1313     rgbdev->clip(state);
1314     clip1dev->clip(state);
1315 }
1316 void BitmapOutputDev::eoClip(GfxState *state)
1317 {
1318     msg("<debug> eoClip");
1319     boolpolydev->eoClip(state);
1320     booltextdev->eoClip(state);
1321     rgbdev->eoClip(state);
1322     clip1dev->eoClip(state);
1323 }
1324 void BitmapOutputDev::clipToStrokePath(GfxState *state)
1325 {
1326     msg("<debug> clipToStrokePath");
1327     boolpolydev->clipToStrokePath(state);
1328     booltextdev->clipToStrokePath(state);
1329     rgbdev->clipToStrokePath(state);
1330     clip1dev->clipToStrokePath(state);
1331 }
1332
1333 void BitmapOutputDev::beginStringOp(GfxState *state)
1334 {
1335     msg("<debug> beginStringOp");
1336     clip0dev->beginStringOp(state);
1337     clip1dev->beginStringOp(state);
1338     booltextdev->beginStringOp(state);
1339     gfxdev->beginStringOp(state);
1340 }
1341 void BitmapOutputDev::beginString(GfxState *state, GString *s)
1342 {
1343     msg("<debug> beginString");
1344     clip0dev->beginString(state, s);
1345     clip1dev->beginString(state, s);
1346     booltextdev->beginString(state, s);
1347     gfxdev->beginString(state, s);
1348 }
1349
1350 void BitmapOutputDev::clearClips()
1351 {
1352     clearBooleanBitmap(clip0bitmap, 0, 0, 0, 0);
1353     clearBooleanBitmap(clip1bitmap, 0, 0, 0, 0);
1354 }
1355 void BitmapOutputDev::clearBoolPolyDev(int x1, int y1, int x2, int y2)
1356 {
1357     clearBooleanBitmap(boolpolybitmap, x1, y1, x2, y2);
1358 }
1359 void BitmapOutputDev::clearBoolTextDev(int x1, int y1, int x2, int y2)
1360 {
1361     clearBooleanBitmap(booltextbitmap, x1, y1, x2, y2);
1362 }
1363 void BitmapOutputDev::drawChar(GfxState *state, double x, double y,
1364                       double dx, double dy,
1365                       double originX, double originY,
1366                       CharCode code, int nBytes, Unicode *u, int uLen)
1367 {
1368     msg("<debug> drawChar render=%d", state->getRender());
1369
1370     if(state->getRender()&RENDER_CLIP) {
1371         //char is just a clipping boundary
1372         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1373         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1374         booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1375         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1376     } else if(rgbbitmap != rgbdev->getBitmap()) {
1377         // we're doing softmasking or transparency grouping
1378         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1379         //checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1380         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1381     } else {
1382         // we're drawing a regular char
1383         clearClips();
1384         clip0dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1385         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1386    
1387         /* calculate the bbox of this character */
1388         int x1 = (int)x, x2 = (int)x+1, y1 = (int)y, y2 = (int)y+1;
1389         SplashFont*font = clip0dev->getCurrentFont();
1390         SplashPath*path = font?font->getGlyphPath(code):NULL;
1391
1392         if(!path) {
1393             if(code)
1394                 msg("<error> couldn't create outline for char %d", code);
1395             return;
1396         }
1397         x-=originX;
1398         y-=originY;
1399         path->offset((SplashCoord)x, (SplashCoord)y);
1400         int t;
1401         for(t=0;t<path->getLength();t++) {
1402             double xx,yy;
1403             Guchar f;
1404             path->getPoint(t,&xx,&yy,&f);
1405             state->transform(xx,yy,&xx,&yy);
1406             if(xx<x1) x1=(int)xx;
1407             if(yy<y1) y1=(int)yy;
1408             if(xx>=x2) x2=(int)xx+1;
1409             if(yy>=y2) y2=(int)yy+1;
1410         }
1411
1412         /* if this character is affected somehow by the various clippings (i.e., it looks
1413            different on a device without clipping), then draw it on the bitmap, not as
1414            text */
1415         if(clip0and1differ(x1,y1,x2,y2)) {
1416             msg("<verbose> Char %d is affected by clipping", code);
1417             boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1418             checkNewBitmap(x1,y1,x2,y2);
1419             rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1420             if(config_extrafontdata) {
1421                 int oldrender = state->getRender();
1422                 state->setRender(3); //invisible
1423                 gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1424                 state->setRender(oldrender);
1425             }
1426         } else {
1427
1428             /* this char is not at all affected by clipping. 
1429                Now just dump out the bitmap we're currently working on, if necessary. */
1430             booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1431             checkNewText(x1,y1,x2,y2);
1432             /* use polygonal output device to do the actual text handling */
1433             gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1434         }
1435     }
1436 }
1437 void BitmapOutputDev::drawString(GfxState *state, GString *s)
1438 {
1439     msg("<error> internal error: drawString not implemented");
1440     return;
1441     clip0dev->drawString(state, s);
1442     clip1dev->drawString(state, s);
1443     booltextdev->drawString(state, s);
1444     gfxdev->drawString(state, s);
1445 }
1446 void BitmapOutputDev::endTextObject(GfxState *state)
1447 {
1448     msg("<debug> endTextObject");
1449     rgbdev->endTextObject(state);
1450     clip0dev->endTextObject(state);
1451     clip1dev->endTextObject(state);
1452     booltextdev->endTextObject(state);
1453     /* the only thing "drawn" here is clipping */
1454     //checkNewText(UNKNOWN_BOUNDING_BOX);
1455     gfxdev->endTextObject(state);
1456 }
1457 void BitmapOutputDev::endString(GfxState *state)
1458 {
1459     msg("<debug> endString");
1460     clip0dev->endString(state);
1461     clip1dev->endString(state);
1462     booltextdev->endString(state);
1463     int render = state->getRender();
1464     if(render != RENDER_INVISIBLE && render != RENDER_FILL) {
1465         checkNewText(UNKNOWN_BOUNDING_BOX);
1466     }
1467     gfxdev->endString(state);
1468 }
1469 void BitmapOutputDev::endStringOp(GfxState *state)
1470 {
1471     msg("<debug> endStringOp");
1472     clip0dev->endStringOp(state);
1473     clip1dev->endStringOp(state);
1474     booltextdev->endStringOp(state);
1475     gfxdev->endStringOp(state);
1476 }
1477
1478 /* TODO: these four operations below *should* do nothing, as type3
1479          chars are drawn using operations like fill() */
1480 GBool BitmapOutputDev::beginType3Char(GfxState *state, double x, double y,
1481                              double dx, double dy,
1482                              CharCode code, Unicode *u, int uLen)
1483 {
1484     msg("<debug> beginType3Char");
1485     /* call gfxdev so that it can generate "invisible" characters
1486        on top of the actual graphic content, for text extraction */
1487     return gfxdev->beginType3Char(state, x, y, dx, dy, code, u, uLen);
1488 }
1489 void BitmapOutputDev::type3D0(GfxState *state, double wx, double wy)
1490 {
1491     msg("<debug> type3D0");
1492     return gfxdev->type3D0(state, wx, wy);
1493 }
1494 void BitmapOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
1495 {
1496     msg("<debug> type3D1");
1497     return gfxdev->type3D1(state, wx, wy, llx, lly, urx, ury);
1498 }
1499 void BitmapOutputDev::endType3Char(GfxState *state)
1500 {
1501     msg("<debug> endType3Char");
1502     gfxdev->endType3Char(state);
1503 }
1504
1505 class CopyStream: public Object
1506 {
1507     Dict*dict;
1508     char*buf;
1509     MemStream*memstream;
1510     public:
1511     CopyStream(Stream*str, int len)
1512     {
1513         buf = 0;
1514         str->reset();
1515         if(len) {
1516             buf = (char*)malloc(len);
1517             int t;
1518             for (t=0; t<len; t++)
1519               buf[t] = str->getChar();
1520         }
1521         str->close();
1522         this->dict = str->getDict();
1523         this->memstream = new MemStream(buf, 0, len, this);
1524     }
1525     ~CopyStream() 
1526     {
1527         ::free(this->buf);this->buf = 0;
1528         delete this->memstream;
1529     }
1530     Dict* getDict() {return dict;}
1531     Stream* getStream() {return this->memstream;};
1532 };
1533
1534 gfxbbox_t BitmapOutputDev::getImageBBox(GfxState*state)
1535 {
1536     gfxbbox_t bbox;
1537     double x,y;
1538     state->transform(0, 1, &x, &y);
1539     bbox.xmin=bbox.xmax = x;
1540     bbox.ymin=bbox.ymax = x;
1541     state->transform(0, 0, &x, &y);
1542     bbox.xmin=min(bbox.xmin,x);
1543     bbox.ymin=min(bbox.ymin,y);
1544     bbox.xmax=max(bbox.xmin,x);
1545     bbox.ymax=max(bbox.ymin,y);
1546     state->transform(1, 0, &x, &y);
1547     bbox.xmin=min(bbox.xmin,x);
1548     bbox.ymin=min(bbox.ymin,y);
1549     bbox.xmax=max(bbox.xmin,x);
1550     bbox.ymax=max(bbox.ymin,y);
1551     state->transform(1, 1, &x, &y);
1552     bbox.xmin=min(bbox.xmin,x);
1553     bbox.ymin=min(bbox.ymin,y);
1554     bbox.xmax=max(bbox.xmin,x);
1555     bbox.ymax=max(bbox.ymin,y);
1556     return bbox;
1557 }
1558 void BitmapOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1559                            int width, int height, GBool invert,
1560                            GBool inlineImg)
1561 {
1562     msg("<debug> drawImageMask streamkind=%d", str->getKind());
1563     CopyStream*cpystr = 0;
1564     if(inlineImg) {
1565         cpystr = new CopyStream(str, height * ((width + 7) / 8));
1566         str = cpystr->getStream();
1567     }
1568     boolpolydev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1569     gfxbbox_t bbox = getImageBBox(state);
1570     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1571     rgbdev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1572     if(cpystr)
1573         delete cpystr;
1574 }
1575 void BitmapOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1576                        int width, int height, GfxImageColorMap *colorMap,
1577                        int *maskColors, GBool inlineImg)
1578 {
1579     msg("<debug> drawImage streamkind=%d", str->getKind());
1580     CopyStream*cpystr = 0;
1581     if(inlineImg) {
1582         cpystr = new CopyStream(str, height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8));
1583         str = cpystr->getStream();
1584     }
1585     boolpolydev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1586     gfxbbox_t bbox=getImageBBox(state);
1587     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1588     rgbdev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1589     if(cpystr)
1590         delete cpystr;
1591 }
1592 void BitmapOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1593                              int width, int height,
1594                              GfxImageColorMap *colorMap,
1595                              Stream *maskStr, int maskWidth, int maskHeight,
1596                              GBool maskInvert)
1597 {
1598     msg("<debug> drawMaskedImage streamkind=%d", str->getKind());
1599     boolpolydev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1600     gfxbbox_t bbox=getImageBBox(state);
1601     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1602     rgbdev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1603 }
1604 void BitmapOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1605                                  int width, int height,
1606                                  GfxImageColorMap *colorMap,
1607                                  Stream *maskStr,
1608                                  int maskWidth, int maskHeight,
1609                                  GfxImageColorMap *maskColorMap)
1610 {
1611     msg("<debug> drawSoftMaskedImage %dx%d (%dx%d) streamkind=%d", width, height, maskWidth, maskHeight, str->getKind());
1612     boolpolydev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1613     gfxbbox_t bbox=getImageBBox(state);
1614     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1615     rgbdev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1616 }
1617 void BitmapOutputDev::drawForm(Ref id)
1618 {
1619     msg("<debug> drawForm");
1620     boolpolydev->drawForm(id);
1621     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1622     rgbdev->drawForm(id);
1623 }
1624
1625 void BitmapOutputDev::processLink(Link *link, Catalog *catalog)
1626 {
1627     msg("<debug> processLink");
1628     gfxdev->processLink(link, catalog);
1629 }
1630
1631 void BitmapOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1632                                     GfxColorSpace *blendingColorSpace,
1633                                     GBool isolated, GBool knockout,
1634                                     GBool forSoftMask)
1635 {
1636     msg("<debug> beginTransparencyGroup");
1637 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1638     GfxState*state1 = state->copy();
1639     GfxState*state2 = state->copy();
1640     state1->setPath(0);
1641     state1->setPath(state->getPath()->copy());
1642     state2->setPath(0);
1643     state2->setPath(state->getPath()->copy());
1644 #else
1645     GfxState*state1 = state->copy(gTrue);
1646     GfxState*state2 = state->copy(gTrue);
1647 #endif
1648     boolpolydev->beginTransparencyGroup(state1, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1649     rgbdev->beginTransparencyGroup(state2, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1650     clip1dev->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1651     delete state1;
1652     delete state2;
1653 }
1654 void BitmapOutputDev::endTransparencyGroup(GfxState *state)
1655 {
1656     msg("<debug> endTransparencyGroup");
1657 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1658     GfxState*state1 = state->copy();
1659     GfxState*state2 = state->copy();
1660     state1->setPath(0);
1661     state1->setPath(state->getPath()->copy());
1662     state2->setPath(0);
1663     state2->setPath(state->getPath()->copy());
1664 #else
1665     GfxState*state1 = state->copy(gTrue);
1666     GfxState*state2 = state->copy(gTrue);
1667 #endif
1668     boolpolydev->endTransparencyGroup(state1);
1669     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1670     rgbdev->endTransparencyGroup(state2);
1671     delete state1;
1672     delete state2;
1673     clip1dev->endTransparencyGroup(state);
1674 }
1675 void BitmapOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1676 {
1677     msg("<debug> paintTransparencyGroup");
1678     boolpolydev->paintTransparencyGroup(state,bbox);
1679     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1680     rgbdev->paintTransparencyGroup(state,bbox);
1681     clip1dev->paintTransparencyGroup(state,bbox);
1682 }
1683 void BitmapOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor)
1684 {
1685     msg("<debug> setSoftMask");
1686     boolpolydev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1687     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1688     rgbdev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1689     clip1dev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1690 }
1691 void BitmapOutputDev::clearSoftMask(GfxState *state)
1692 {
1693     msg("<debug> clearSoftMask");
1694     boolpolydev->clearSoftMask(state);
1695     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1696     rgbdev->clearSoftMask(state);
1697     clip1dev->clearSoftMask(state);
1698 }