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