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