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