void QGIFFormat::disposePrevious( QImage& img, QImageConsumer* consumer ) { if ( out_of_bounds ) // flush anything that survived consumer->changed(QRect(0,0,swidth,sheight)); // Handle disposal of previous image before processing next one if ( disposed ) return; int l = QMIN(swidth-1,left); int r = QMIN(swidth-1,right); int t = QMIN(sheight-1,top); int b = QMIN(sheight-1,bottom); switch (disposal) { case NoDisposal: break; case DoNotChange: break; case RestoreBackground: if (trans_index>=0) { // Easy: we use the transparent color fillRect(img, l, t, r-l+1, b-t+1, Q_TRANSPARENT); } else if (bgcol>=0) { // Easy: we use the bgcol given fillRect(img, l, t, r-l+1, b-t+1, color(bgcol)); } else { // Impossible: We don't know of a bgcol - use pixel 0 QRgb** line = (QRgb **)img.jumpTable(); fillRect(img, l, t, r-l+1, b-t+1, line[0][0]); } if (consumer) consumer->changed(QRect(l, t, r-l+1, b-t+1)); break; case RestoreImage: { if ( frame > 0 ) { QRgb** line = (QRgb **)img.jumpTable(); for (int ln=t; ln<=b; ln++) { memcpy(line[ln]+l, backingstore.scanLine(ln-t), (r-l+1)*sizeof(QRgb) ); } consumer->changed(QRect(l, t, r-l+1, b-t+1)); } } } disposal = NoDisposal; // Until an extension says otherwise. disposed = TRUE; }
void writePNG(const QImage& image) { info_ptr->channels = 4; png_set_sig_bytes(png_ptr, 8); // Pretend we already wrote the sig png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), 8, image.hasAlphaBuffer() ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, 0, 0, 0); png_write_info(png_ptr, info_ptr); if ( !image.hasAlphaBuffer() ) png_set_filler(png_ptr, 0, QImage::systemByteOrder() == QImage::BigEndian ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); //if ( QImage::systemByteOrder() == QImage::BigEndian ) { //png_set_swap_alpha(png_ptr); //} if ( QImage::systemByteOrder() == QImage::LittleEndian ) { png_set_bgr(png_ptr); } png_bytep* row_pointers; uint height = image.height(); uchar** jt = image.jumpTable(); row_pointers=new png_bytep[height]; uint y; for (y=0; y<height; y++) { row_pointers[y]=jt[y]; } png_write_image(png_ptr, row_pointers); delete [] row_pointers; png_write_end(png_ptr, info_ptr); end_png(); begin_png(); }
void QAnimationWriter::appendFrame(const QImage& frm, const QPoint& offset) { QImage frame = frm.convertDepth(32); const int alignx = 1; if ( dev ) { if ( prev.isNull() || !d->canCompose() ) { d->setImage(frame); } else { bool done; int minx, maxx, miny, maxy; int w = frame.width(); int h = frame.height(); QRgb** jt = (QRgb**)frame.jumpTable(); QRgb** pjt = (QRgb**)prev.jumpTable() + offset.y(); // Find left edge of change done = FALSE; for (minx = 0; minx < w && !done; minx++) { for (int ty = 0; ty < h; ty++) { if ( jt[ty][minx] != pjt[ty][minx+offset.x()] ) { done = TRUE; break; } } } minx--; // Find right edge of change done = FALSE; for (maxx = w-1; maxx >= 0 && !done; maxx--) { for (int ty = 0; ty < h; ty++) { if ( jt[ty][maxx] != pjt[ty][maxx+offset.x()] ) { done = TRUE; break; } } } maxx++; // Find top edge of change done = FALSE; for (miny = 0; miny < h && !done; miny++) { for (int tx = 0; tx < w; tx++) { if ( jt[miny][tx] != pjt[miny][tx+offset.x()] ) { done = TRUE; break; } } } miny--; // Find right edge of change done = FALSE; for (maxy = h-1; maxy >= 0 && !done; maxy--) { for (int tx = 0; tx < w; tx++) { if ( jt[maxy][tx] != pjt[maxy][tx+offset.x()] ) { done = TRUE; break; } } } maxy++; if ( minx > maxx ) minx=maxx=0; if ( miny > maxy ) miny=maxy=0; if ( alignx > 1 ) { minx -= minx % alignx; maxx = maxx - maxx % alignx + alignx - 1; } int dw = maxx-minx+1; int dh = maxy-miny+1; QImage diff(dw, dh, 32); diff.setAlphaBuffer(TRUE); int x, y; for (y = 0; y < dh; y++) { QRgb* li = (QRgb*)frame.scanLine(y+miny)+minx; QRgb* lp = (QRgb*)prev.scanLine(y+miny+offset.y())+minx+offset.x(); QRgb* ld = (QRgb*)diff.scanLine(y); if ( alignx ) { for (x = 0; x < dw; x+=alignx) { int i; for (i=0; i<alignx; i++) { if ( li[x+i] != lp[x+i] ) break; } if ( i == alignx ) { // All the same for (i=0; i<alignx; i++) { ld[x+i] = qRgba(0,0,0,0); } } else { // Some different for (i=0; i<alignx; i++) { ld[x+i] = 0xff000000 | li[x+i]; } } } } else { for (x = 0; x < dw; x++) { if ( li[x] != lp[x] ) ld[x] = 0xff000000 | li[x]; else ld[x] = qRgba(0,0,0,0); } } } qDebug("%d,%d %d,%d",minx,miny,offset.x(),offset.y()); d->composeImage(diff,QPoint(minx,miny)+offset); } if ( prev.isNull() || prev.size() == frame.size() && offset == QPoint(0,0) ) { prev = frame; } else { bitBlt(&prev,offset.x(),offset.y(),&frame,0,0,frame.width(),frame.height()); } } }
QImage splash_read_jpeg_image(FILE* f) { QImage image; struct jpeg_decompress_struct cinfo; struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(f); struct my_error_mgr jerr; jpeg_create_decompress(&cinfo); cinfo.src = iod_src; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = my_error_exit; if (!setjmp(jerr.setjmp_buffer)) { #if defined(Q_OS_UNIXWARE) (void) jpeg_read_header(&cinfo, B_TRUE); #else (void) jpeg_read_header(&cinfo, TRUE); #endif (void) jpeg_start_decompress(&cinfo); { bool created = FALSE; if ( cinfo.output_components == 3 || cinfo.output_components == 4) { created = image.create( cinfo.output_width, cinfo.output_height, 32 ); } else if ( cinfo.output_components == 1 ) { created = image.create( cinfo.output_width, cinfo.output_height, 8, 256 ); for (int i=0; i<256; i++) image.setColor(i, qRgb(i,i,i)); } else { // Unsupported format } if (!created) image = QImage(); if (!image.isNull()) { uchar** lines = image.jumpTable(); while (cinfo.output_scanline < cinfo.output_height) (void) jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height); (void) jpeg_finish_decompress(&cinfo); if ( cinfo.output_components == 3 ) { // Expand 24->32 bpp. for (uint j=0; j<cinfo.output_height; j++) { uchar *in = image.scanLine(j) + cinfo.output_width * 3; QRgb *out = (QRgb*)image.scanLine(j); for (uint i=cinfo.output_width; i--; ) { in-=3; out[i] = qRgb(in[0], in[1], in[2]); } } } } } if (!image.isNull()) { if ( cinfo.density_unit == 1 ) { image.setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) ); image.setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) ); } else if ( cinfo.density_unit == 2 ) { image.setDotsPerMeterX( int(100. * cinfo.X_density) ); image.setDotsPerMeterY( int(100. * cinfo.Y_density) ); } } } jpeg_destroy_decompress(&cinfo); delete iod_src; return image; }
static void read_png_image(QImageIO* iio) { png_structp png_ptr; png_infop info_ptr; png_infop end_info; png_bytep* row_pointers; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { iio->setStatus(-1); return; } png_set_error_fn(png_ptr, 0, 0, qt_png_warning); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, 0, 0); iio->setStatus(-2); return; } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); iio->setStatus(-3); return; } if (setjmp(png_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); iio->setStatus(-4); return; } png_set_read_fn(png_ptr, (void*)iio, iod_read_fn); png_read_info(png_ptr, info_ptr); QImage image; setup_qt(image, png_ptr, info_ptr, iio->gamma()); if (image.isNull()) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); iio->setStatus(-5); return; } png_uint_32 width; png_uint_32 height; int bit_depth; int color_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); uchar** jt = image.jumpTable(); row_pointers=new png_bytep[height]; for (uint y=0; y<height; y++) { row_pointers[y]=jt[y]; } png_read_image(png_ptr, row_pointers); #if 0 // libpng takes care of this. png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) if (image.depth()==32 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { QRgb trans = 0xFF000000 | qRgb( (info_ptr->trans_values.red << 8 >> bit_depth)&0xff, (info_ptr->trans_values.green << 8 >> bit_depth)&0xff, (info_ptr->trans_values.blue << 8 >> bit_depth)&0xff); for (uint y=0; y<height; y++) { for (uint x=0; x<info_ptr->width; x++) { if (((uint**)jt)[y][x] == trans) { ((uint**)jt)[y][x] &= 0x00FFFFFF; } else { } } } }
static void read_jpeg_image(QImageIO* iio) { QImage image; struct jpeg_decompress_struct cinfo; struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(iio); struct my_error_mgr jerr; jpeg_create_decompress(&cinfo); cinfo.src = iod_src; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = my_error_exit; if (!setjmp(jerr.setjmp_buffer)) { #if defined(_OS_UNIXWARE7_) (void) jpeg_read_header(&cinfo, B_TRUE); #else (void) jpeg_read_header(&cinfo, TRUE); #endif (void) jpeg_start_decompress(&cinfo); if ( cinfo.output_components == 3 || cinfo.output_components == 4) { image.create( cinfo.output_width, cinfo.output_height, 32 ); } else if ( cinfo.output_components == 1 ) { image.create( cinfo.output_width, cinfo.output_height, 8, 256 ); for (int i=0; i<256; i++) image.setColor(i, qRgb(i,i,i)); } else { // Unsupported format } if (!image.isNull()) { uchar** lines = image.jumpTable(); while (cinfo.output_scanline < cinfo.output_height) (void) jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height); (void) jpeg_finish_decompress(&cinfo); } if ( cinfo.output_components == 3 ) { // Expand 24->32 bpp. for (uint j=0; j<cinfo.output_height; j++) { uchar *in = image.scanLine(j) + cinfo.output_width * 3; QRgb *out = (QRgb*)image.scanLine(j); for (uint i=cinfo.output_width; i--; ) { in-=3; out[i] = qRgb(in[0], in[1], in[2]); } } } iio->setImage(image); iio->setStatus(0); } jpeg_destroy_decompress(&cinfo); delete iod_src; }
/*! This function decodes some data into image changes. Returns the number of bytes consumed. */ int QGIFFormat::decode(QImage& img, QImageConsumer* consumer, const uchar* buffer, int length) { // We are required to state that // "The Graphics Interchange Format(c) is the Copyright property of // CompuServe Incorporated. GIF(sm) is a Service Mark property of // CompuServe Incorporated." #define LM(l, m) (((m)<<8)|l) digress = FALSE; int initial = length; QRgb** line = (QRgb **)img.jumpTable(); while (!digress && length) { length--; unsigned char ch=*buffer++; switch (state) { case Header: hold[count++]=ch; if (count==6) { // Header gif89=(hold[3]!='8' || hold[4]!='7'); state=LogicalScreenDescriptor; count=0; } break; case LogicalScreenDescriptor: hold[count++]=ch; if (count==7) { // Logical Screen Descriptor swidth=LM(hold[0], hold[1]); sheight=LM(hold[2], hold[3]); gcmap=!!(hold[4]&0x80); //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1); //UNUSED: gcmsortflag=!!(hold[4]&0x08); gncols=2<<(hold[4]&0x7); bgcol=(gcmap) ? hold[5] : -1; //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0; trans_index = -1; count=0; ncols=gncols; if (gcmap) { ccount=0; state=GlobalColorMap; globalcmap = new QRgb[gncols+1]; // +1 for trans_index globalcmap[gncols] = Q_TRANSPARENT; } else { state=Introducer; } } break; case GlobalColorMap: case LocalColorMap: hold[count++]=ch; if (count==3) { QRgb rgb = qRgb(hold[0], hold[1], hold[2]); if ( state == LocalColorMap ) { if ( ccount < lncols ) localcmap[ccount] = rgb; } else { globalcmap[ccount] = rgb; } if (++ccount >= ncols) { if ( state == LocalColorMap ) state=TableImageLZWSize; else state=Introducer; } count=0; } break; case Introducer: hold[count++]=ch; switch (ch) { case ',': state=ImageDescriptor; break; case '!': state=ExtensionLabel; break; case ';': if (consumer) { if ( out_of_bounds ) // flush anything that survived consumer->changed(QRect(0,0,swidth,sheight)); consumer->end(); } state=Done; break; default: digress=TRUE; // Unexpected Introducer - ignore block state=Error; } break; case ImageDescriptor: hold[count++]=ch; if (count==10) { int newleft=LM(hold[1], hold[2]); int newtop=LM(hold[3], hold[4]); int width=LM(hold[5], hold[6]); int height=LM(hold[7], hold[8]); // disbelieve ridiculous logical screen sizes, // unless the image frames are also large. if ( swidth/10 > QMAX(width,200) ) swidth = -1; if ( sheight/10 > QMAX(height,200) ) sheight = -1; if ( swidth <= 0 ) swidth = newleft + width; if ( sheight <= 0 ) sheight = newtop + height; if (img.isNull()) { img.create(swidth, sheight, 32); memset( img.bits(), 0, img.numBytes() ); if (consumer) consumer->setSize(swidth, sheight); } img.setAlphaBuffer(trans_index >= 0); line = (QRgb **)img.jumpTable(); disposePrevious( img, consumer ); disposed = FALSE; left = newleft; top = newtop; // Sanity check frame size - must fit on "screen". if (left >= swidth) left=QMAX(0, swidth-1); if (top >= sheight) top=QMAX(0, sheight-1); if (left+width >= swidth) { if ( width <= swidth ) left=swidth-width; else width=swidth-left; } if (top+height >= sheight) { if ( height <= sheight ) top=sheight-height; else height=sheight-top; } right=QMAX( 0, left+width-1); bottom=QMAX(0, top+height-1); lcmap=!!(hold[9]&0x80); interlace=!!(hold[9]&0x40); //bool lcmsortflag=!!(hold[9]&0x20); lncols=lcmap ? (2<<(hold[9]&0x7)) : 0; if (lncols) { if ( localcmap ) delete [] localcmap; localcmap = new QRgb[lncols+1]; localcmap[lncols] = Q_TRANSPARENT; ncols = lncols; } else { ncols = gncols; } frame++; if ( frame == 0 ) { if ( left || top || width!=swidth || height!=sheight ) { // Not full-size image - erase with bg or transparent if ( trans_index >= 0 ) { fillRect(img, 0, 0, swidth, sheight, color(trans_index)); if (consumer) consumer->changed(QRect(0,0,swidth,sheight)); } else if ( bgcol>=0 ) { fillRect(img, 0, 0, swidth, sheight, color(bgcol)); if (consumer) consumer->changed(QRect(0,0,swidth,sheight)); } } } if ( disposal == RestoreImage ) { int l = QMIN(swidth-1,left); int r = QMIN(swidth-1,right); int t = QMIN(sheight-1,top); int b = QMIN(sheight-1,bottom); int w = r-l+1; int h = b-t+1; if (backingstore.width() < w || backingstore.height() < h) { // We just use the backing store as a byte array backingstore.create( QMAX(backingstore.width(), w), QMAX(backingstore.height(), h), 32); memset( img.bits(), 0, img.numBytes() ); } for (int ln=0; ln<h; ln++) { memcpy(backingstore.scanLine(ln), line[t+ln]+l, w*sizeof(QRgb)); } } count=0; if (lcmap) { ccount=0; state=LocalColorMap; } else { state=TableImageLZWSize; } x = left; y = top; accum = 0; bitcount = 0; sp = stack; needfirst = FALSE; out_of_bounds = FALSE; } break; case TableImageLZWSize: { lzwsize=ch; if ( lzwsize > max_lzw_bits ) { state=Error; } else { code_size=lzwsize+1; clear_code=1<<lzwsize; end_code=clear_code+1; max_code_size=2*clear_code; max_code=clear_code+2; int i; for (i=0; i<clear_code && i<(1<<max_lzw_bits); i++) { table[0][i]=0; table[1][i]=i; } for (i=clear_code; i<(1<<max_lzw_bits); i++) { table[0][i]=table[1][i]=0; } state=ImageDataBlockSize; } count=0; break; } case ImageDataBlockSize: expectcount=ch; if (expectcount) { state=ImageDataBlock; } else { if (consumer) { consumer->frameDone(); digress = TRUE; } state=Introducer; } break; case ImageDataBlock: count++; accum|=(ch<<bitcount); bitcount+=8; while (bitcount>=code_size && state==ImageDataBlock) { int code=accum&((1<<code_size)-1); bitcount-=code_size; accum>>=code_size; if (code==clear_code) { if (!needfirst) { int i; code_size=lzwsize+1; max_code_size=2*clear_code; max_code=clear_code+2; for (i=0; i<clear_code; i++) { table[0][i]=0; table[1][i]=i; } for (i=clear_code; i<(1<<max_lzw_bits); i++) { table[0][i]=table[1][i]=0; } } needfirst=TRUE; } else if (code==end_code) { bitcount = -32768; // Left the block end arrive } else { if (needfirst) { firstcode=oldcode=code; if (!out_of_bounds && line && firstcode!=trans_index) line[y][x] = color(firstcode); x++; if (x>=swidth) out_of_bounds = TRUE; needfirst=FALSE; if (x>right) { x=left; if (out_of_bounds) out_of_bounds = left>=swidth || y>=sheight; nextY(img,consumer); } } else { incode=code; if (code>=max_code) { *sp++=firstcode; code=oldcode; } while (code>=clear_code) { *sp++=table[1][code]; if (code==table[0][code]) { state=Error; break; } if (sp-stack>=(1<<(max_lzw_bits))*2) { state=Error; break; } code=table[0][code]; } *sp++=firstcode=table[1][code]; code=max_code; if (code<(1<<max_lzw_bits)) { table[0][code]=oldcode; table[1][code]=firstcode; max_code++; if ((max_code>=max_code_size) && (max_code_size<(1<<max_lzw_bits))) { max_code_size*=2; code_size++; } } oldcode=incode; while (sp>stack) { --sp; if (!out_of_bounds && *sp!=trans_index) line[y][x] = color(*sp); x++; if (x>=swidth) out_of_bounds = TRUE; if (x>right) { x=left; if (out_of_bounds) out_of_bounds = left>=swidth || y>=sheight; nextY(img,consumer); } } } } } if (count==expectcount) { count=0; state=ImageDataBlockSize; } break; case ExtensionLabel: switch (ch) { case 0xf9: state=GraphicControlExtension; break; case 0xff: state=ApplicationExtension; break; #if 0 case 0xfe: state=CommentExtension; break; case 0x01: break; #endif default: state=SkipBlockSize; } count=0; break; case ApplicationExtension: if (count<11) hold[count]=ch; count++; if (count==hold[0]+1) { if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) { // Looping extension state=NetscapeExtensionBlockSize; } else { state=SkipBlockSize; } count=0; } break; case NetscapeExtensionBlockSize: expectcount=ch; count=0; if (expectcount) state=NetscapeExtensionBlock; else state=Introducer; break; case NetscapeExtensionBlock: if (count<3) hold[count]=ch; count++; if (count==expectcount) { int loop = hold[0]+hold[1]*256; if (consumer) consumer->setLooping(loop); state=SkipBlockSize; // Ignore further blocks } break; case GraphicControlExtension: if (count<5) hold[count]=ch; count++; if (count==hold[0]+1) { disposePrevious( img, consumer ); disposal=Disposal((hold[1]>>2)&0x7); //UNUSED: waitforuser=!!((hold[1]>>1)&0x1); int delay=count>3 ? LM(hold[2], hold[3]) : 1; // IE and mozilla use a minimum delay of 10. With the minumum delay of 10 // we are compatible to them and avoid huge loads on the app and xserver. if ( delay < 10 ) delay = 10; bool havetrans=hold[1]&0x1; trans_index = havetrans ? hold[4] : -1; if (consumer) consumer->setFramePeriod(delay*10); count=0; state=SkipBlockSize; } break; case SkipBlockSize: expectcount=ch; count=0; if (expectcount) state=SkipBlock; else state=Introducer; break; case SkipBlock: count++; if (count==expectcount) state=SkipBlockSize; break; case Done: digress=TRUE; /* Netscape ignores the junk, so we do too. length++; // Unget state=Error; // More calls to this is an error */ break; case Error: return -1; // Called again after done. } }