APNGDATA * loadPng(IPngReader *pSrc) { png_bytep dataFrame; png_uint_32 bytesPerRow; png_uint_32 bytesPerFrame; png_bytepp rowPointers; png_byte sig[8]; png_structp png_ptr_read; png_infop info_ptr_read; pSrc->read(sig,8); if(!png_check_sig(sig,8)) { return NULL; } png_ptr_read = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); info_ptr_read = png_create_info_struct(png_ptr_read); if (setjmp(png_jmpbuf(png_ptr_read))) { png_destroy_read_struct(&png_ptr_read, &info_ptr_read, NULL); return NULL; } png_set_read_fn(png_ptr_read,pSrc,mypng_read_data); png_set_sig_bytes(png_ptr_read, 8); if ((png_ptr_read->bit_depth < 8) || (png_ptr_read->color_type == PNG_COLOR_TYPE_PALETTE) || (info_ptr_read->valid & PNG_INFO_tRNS)) png_set_expand(png_ptr_read); png_set_add_alpha(png_ptr_read, 0xff, PNG_FILLER_AFTER); png_set_interlace_handling(png_ptr_read); png_set_gray_to_rgb(png_ptr_read); png_set_strip_16(png_ptr_read); png_read_info(png_ptr_read, info_ptr_read); png_read_update_info(png_ptr_read, info_ptr_read); bytesPerRow = png_ptr_read->width * 4; bytesPerFrame = bytesPerRow * png_ptr_read->height; APNGDATA * apng = (APNGDATA*) malloc(sizeof(APNGDATA)); memset(apng,0,sizeof(APNGDATA)); apng->nWid = png_ptr_read->width; apng->nHei = png_ptr_read->height; //图像帧数据 dataFrame = (png_bytep)malloc(bytesPerRow * apng->nHei); memset(dataFrame,0,bytesPerFrame); //获得扫描行指针 rowPointers = (png_bytepp)malloc(sizeof(png_bytep)* apng->nHei); for(int i=0;i<apng->nHei;i++) rowPointers[i] = dataFrame + bytesPerRow * i; if (!png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_acTL)) {//load png doesn't has this trunk. png_read_image(png_ptr_read,rowPointers); apng->pdata =dataFrame; apng->nFrames =1; }else {//load apng apng->nFrames = png_get_num_frames(png_ptr_read, info_ptr_read);//获取总帧数 png_bytep data = (png_bytep)malloc( bytesPerFrame * apng->nFrames);//为每一帧分配内存 png_bytep curFrame = (png_bytep)malloc(bytesPerFrame); memset(curFrame,0,bytesPerFrame); apng->nLoops = png_get_num_plays(png_ptr_read, info_ptr_read); apng->pDelay = (unsigned short*)malloc(sizeof(unsigned short)*apng->nFrames); for(int iFrame = 0;iFrame<apng->nFrames;iFrame++) { //读帧信息头 png_read_frame_head(png_ptr_read, info_ptr_read); //计算出帧延时信息 if (png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_fcTL)) { png_uint_16 delay_num = info_ptr_read->next_frame_delay_num, delay_den = info_ptr_read->next_frame_delay_den; if (delay_den==0 || delay_den==100) apng->pDelay[iFrame] = delay_num; else if (delay_den==10) apng->pDelay[iFrame] = delay_num*10; else if (delay_den==1000) apng->pDelay[iFrame] = delay_num/10; else apng->pDelay[iFrame] = delay_num*100/delay_den; }else { apng->pDelay[iFrame] = 0; } //读取PNG帧到dataFrame中,不含偏移数据 png_read_image(png_ptr_read, rowPointers); {//将当前帧数据绘制到当前显示帧中:1)获得绘制的背景;2)计算出绘制位置; 3)使用指定的绘制方式与背景混合 //1)计算出绘制位置 png_bytep lineDst=curFrame+info_ptr_read->next_frame_y_offset*bytesPerRow + 4 * info_ptr_read->next_frame_x_offset; png_bytep lineSour=dataFrame; //2)使用指定的绘制方式与背景混合 switch(info_ptr_read->next_frame_blend_op) { case PNG_BLEND_OP_OVER: { for(unsigned int y=0;y<info_ptr_read->next_frame_height;y++) { png_bytep lineDst1=lineDst; png_bytep lineSour1=lineSour; for(unsigned int x=0;x<info_ptr_read->next_frame_width;x++) { png_byte alpha = lineSour1[3]; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; } lineDst += bytesPerRow; lineSour+= bytesPerRow; } } break; case PNG_BLEND_OP_SOURCE: { for(unsigned int y=0;y<info_ptr_read->next_frame_height;y++) { memcpy(lineDst,lineSour,info_ptr_read->next_frame_width*4); lineDst += bytesPerRow; lineSour+= bytesPerRow; } } break; default: SASSERT(FALSE); break; } png_bytep targetFrame = data + bytesPerFrame * iFrame; memcpy(targetFrame,curFrame,bytesPerFrame); lineDst=curFrame+info_ptr_read->next_frame_y_offset*bytesPerRow + 4 * info_ptr_read->next_frame_x_offset; //3)处理当前帧绘制区域 switch(info_ptr_read->next_frame_dispose_op) { case PNG_DISPOSE_OP_BACKGROUND://clear background { for(unsigned int y=0;y<info_ptr_read->next_frame_height;y++) { memset(lineDst,0,info_ptr_read->next_frame_width*4); lineDst += bytesPerRow; } } break; case PNG_DISPOSE_OP_PREVIOUS://copy previous frame if(iFrame>0) { memcpy(curFrame,targetFrame-bytesPerFrame,bytesPerFrame); } break; case PNG_DISPOSE_OP_NONE://using current frame, doing nothing break; default: SASSERT(0); break; } } } free(curFrame); free(dataFrame); apng->pdata =data; } free(rowPointers); png_read_end(png_ptr_read,info_ptr_read); png_destroy_read_struct(&png_ptr_read, &info_ptr_read, NULL); return apng; }
static void readAnimated( imageCache &cache, PngInfo& png ){ auto width = png.width(); auto height = png.height(); png_uint_32 x_offset=0, y_offset=0; png_uint_16 delay_num=0, delay_den=0; png_byte dispose_op = PNG_DISPOSE_OP_NONE, blend_op = PNG_BLEND_OP_SOURCE; QImage canvas( width, height, QImage::Format_ARGB32 ); canvas.fill( qRgba( 0,0,0,0 ) ); AnimCombiner combiner( canvas ); if( setjmp( png_jmpbuf( png.png ) ) ) return; unsigned repeats = png_get_num_plays( png.png, png.info ); unsigned frames = png_get_num_frames( png.png, png.info ); //NOTE: We discard the frame if it is not a part of the animation if( png_get_first_frame_is_hidden( png.png, png.info ) ){ readImage( png, width, height ); --frames; //libpng appears to tell the total amount of images } cache.set_info( frames, true, repeats>0 ? repeats-1 : -1 ); for( unsigned i=0; i < frames; ++i ){ png_read_frame_head( png.png, png.info ); if( png_get_valid( png.png, png.info, PNG_INFO_fcTL ) ){ png_get_next_frame_fcTL( png.png, png.info , &width, &height , &x_offset, &y_offset , &delay_num, &delay_den , &dispose_op, &blend_op ); } else{ width = png.width(); height = png.height(); } readImage( png, width, height, i==0 ); //Calculate delay delay_den = delay_den==0 ? 100 : delay_den; unsigned delay = std::ceil( (double)delay_num / delay_den * 1000 ); if( delay == 0 ) delay = 1; //Fastest speed we support //Compose and add auto blend_mode = blend_op == PNG_BLEND_OP_SOURCE ? BlendMode::OVERLAY : BlendMode::REPLACE; auto dispose_mode = [=](){ switch( dispose_op ){ case PNG_DISPOSE_OP_NONE: return DisposeMode::NONE; case PNG_DISPOSE_OP_BACKGROUND: return DisposeMode::BACKGROUND; case PNG_DISPOSE_OP_PREVIOUS: return DisposeMode::REVERT; default: return DisposeMode::NONE; //TODO: add error } }(); QImage output = combiner.combine( png.frame, x_offset, y_offset, blend_mode, dispose_mode ); cache.add_frame( output, delay ); } }
void Openchunk::run() { png_structp png_ptr_read; png_infop info_ptr_read; png_structp png_ptr_write; png_infop info_ptr_write; png_uint_32 next_frame_width; png_uint_32 next_frame_height; png_uint_32 next_frame_x_offset; png_uint_32 next_frame_y_offset; png_uint_16 next_frame_delay_num; png_uint_16 next_frame_delay_den; png_byte next_frame_dispose_op; png_byte next_frame_blend_op; png_color_16p Framebg; framlist.clear(); buffer = new StreamFile(); buffer->LoadFile(subfile); qDebug() << "### start read ->" << subfile; if (!buffer->isValid()) { alert("unvalid buffer fill",__LINE__); exit(0); } validpng = buffer->device()->peek(4).contains("PNG"); qDebug() << "### ahed ->" << validpng; frame1.loadFromData(buffer->stream()); /* load first frame */ if (frame1.isNull()) { alert("unable find a valid image",__LINE__); return; } png_ptr_read = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(png_ptr_read == NULL) { alert("unable to create read struct",__LINE__); return; } info_ptr_read = png_create_info_struct(png_ptr_read); if(info_ptr_read == NULL) { alert("unable to create info struct",__LINE__); return; } png_set_read_fn(png_ptr_read,buffer,EncoderReaderCallback); if(setjmp(png_ptr_read->jmpbuf)) { alert("something didn't work, jump 1",__LINE__); return; } png_read_info(png_ptr_read, info_ptr_read); if(!png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_acTL)) { alert("source image must be animated",__LINE__); return; } QRect Frect(0,0,info_ptr_read->width,info_ptr_read->height); ////////////qDebug() << "### Frame rect from head ->" << Frect; QImage master(Frect.width(),Frect.height(),QImage::Format_ARGB32); const uint height = master.height(); png_bytep *row_pointers = new png_bytep[height]; for (uint i = 0; i < height; ++i) { row_pointers[i] = (png_bytep)master.scanLine(i); } int validloop = -1; for(int i = 0; i < png_get_num_frames(png_ptr_read, info_ptr_read); i++) { /////////qDebug() << "### frame read ------------- " << i; png_ptr_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(png_ptr_write == NULL) { alert("unable to create write struct"); return; } StreamFile *framer = new StreamFile(); png_set_write_fn(png_ptr_write,framer,EncoderWriteCallback, NULL); info_ptr_write = png_create_info_struct(png_ptr_write); if(info_ptr_write == NULL) { alert("unable to create write struct"); return; } if(setjmp(png_ptr_write->jmpbuf)) { alert("something didn't work, jump 2"); return; } png_read_frame_head(png_ptr_read, info_ptr_read); if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_fcTL)) { png_get_next_frame_fcTL(png_ptr_read, info_ptr_read, &next_frame_width, &next_frame_height, &next_frame_x_offset, &next_frame_y_offset, &next_frame_delay_num, &next_frame_delay_den, &next_frame_dispose_op, &next_frame_blend_op); } else { /* the first frame doesn't have an fcTL so it's expected to be hidden, * but we'll extract it anyway next_frame_x_offset , next_frame_y_offset */ next_frame_width = png_get_image_width(png_ptr_read, info_ptr_read); next_frame_height = png_get_image_height(png_ptr_read, info_ptr_read); } QRect C_frame_rect(0,0,next_frame_width,next_frame_height); AFRAMES FrameInfo = OneFrameReader(png_ptr_read, info_ptr_read, png_ptr_write, info_ptr_write, next_frame_width, next_frame_height); png_write_info(png_ptr_write, info_ptr_write); png_read_image(png_ptr_read, row_pointers); png_write_image(png_ptr_write, row_pointers); png_write_end(png_ptr_write, NULL); float Fraction = (float)next_frame_delay_num / (float)next_frame_delay_den + 0.00; ////////qDebug() << "### Fraction " << Fraction; int PlayGo; if (Fraction < 0.001 ) { PlayGo = 100; } else if (Fraction < 1.010 && Fraction > 0.9) { PlayGo = 1000; } else { PlayGo = Fraction * 1000; } /* extract frames */ if ( framer->isValid() && Frect.contains(C_frame_rect) ) { validloop++; int Coalpha = 255; /* prepare image if no background find grab a pixel color! */ QImage tmpgd(C_frame_rect.width(),C_frame_rect.height(),QImage::Format_ARGB32); /* compose it files */ APNGFRAME one; one.dimg = framer->stream(); one.maxframe = Frect; one.pos = validloop; one.point = QPoint(next_frame_x_offset,next_frame_y_offset); one.play = PlayGo; if (!(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_bKGD))) { tmpgd.loadFromData(one.dimg); QRgb GrepColor = tmpgd.pixel(QPoint(2,2)); one.bg = QColor(GrepColor); one.bg.setAlpha(qAlpha(GrepColor)); Coalpha = qAlpha(GrepColor); } else { one.bg = FrameInfo.bg; } QImage tmpe; QImage Pvidi(one.maxframe.width(),one.maxframe.height(),QImage::Format_ARGB32); if (tmpe.loadFromData(one.dimg)) { QPainter p(&Pvidi); p.setRenderHint(QPainter::Antialiasing, true); p.setBrush(one.bg); p.drawRect(Pvidi.rect()); p.drawImage(one.point,tmpe); p.end(); one.item = Pvidi; ////qDebug() << "### isNull() in theard .." << one.item.isNull(); } //////item tmpe //////qDebug() << "### Frame rect ->" << one.point << ",nr." << validloop << "C." << one.bg.name() << "Al." << Coalpha; framlist.insert(validloop,one); } framer->~StreamFile(); png_ptr_write = 0; info_ptr_write = 0; } qDebug() << "### Frame size() ->" << framlist.size(); //////alert("end of chunks",__LINE__); validpng = true; buffer->~StreamFile(); exit(0); }
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially) { PNG *png = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; bool apng; unsigned int width; unsigned int height; int color_type; int bit_depth; bool is_opaque; unsigned char* buffer = NULL; unsigned int frame_count = 0; bool hide_first_frame = false; PNG_FRAME_INFO* frame_info_array = NULL; int i; png = (PNG *) malloc(sizeof(PNG)); if (png == NULL) { WTF_OM; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &user_error_fn, &user_warn_fn); if (png_ptr == NULL) { free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, NULL, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } if (setjmp(png_jmpbuf(png_ptr))) { LOGE(EMSG("Error in png decode")); free_frame_info_array(frame_info_array, frame_count); frame_info_array = NULL; free(buffer); buffer = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } // Set custom read function png_set_read_fn(png_ptr, patch_head_input_stream, &user_read_fn); // Get png info png_read_info(png_ptr, info_ptr); // Check apng if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) { apng = true; } else { apng = false; } // PNG info width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); // Create buffer buffer = (unsigned char*) malloc(width * height * 4); if (buffer == NULL) { WTF_OM; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } if (apng) { // Get frame count frame_count = png_get_num_frames(png_ptr, info_ptr); hide_first_frame = png_get_first_frame_is_hidden(png_ptr, info_ptr); if (hide_first_frame) { frame_count--; } // Create frame info array frame_info_array = (PNG_FRAME_INFO*) calloc(frame_count, sizeof(PNG_FRAME_INFO)); if (frame_info_array == NULL) { WTF_OM; free(buffer); buffer = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } } // Configure to ARGB png_set_expand(png_ptr); if (bit_depth == 16) { png_set_scale_16(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } if (!(color_type & PNG_COLOR_MASK_ALPHA)) { is_opaque = true; png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); } else { is_opaque = false; } if (apng) { if (hide_first_frame) { // Skip first frame read_image(png_ptr, buffer, width, height); } // Read first frame read_frame(png_ptr, info_ptr, frame_info_array); // Fix dop if (frame_info_array->dop == PNG_DISPOSE_OP_PREVIOUS) { frame_info_array->dop = PNG_DISPOSE_OP_BACKGROUND; } if (!partially || frame_count == 1) { // Read all frame for (i = 1; i < frame_count; read_frame(png_ptr, info_ptr, frame_info_array + i++)); // Generate pop generate_pop(frame_info_array, frame_count); // End read png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // Close input stream close_patch_head_input_stream(env, patch_head_input_stream); destroy_patch_head_input_stream(env, &patch_head_input_stream); png->partially = false; png->png_ptr = NULL; png->info_ptr = NULL; png->patch_head_input_stream = NULL; } else { png->partially = true; png->png_ptr = png_ptr; png->info_ptr = info_ptr; png->patch_head_input_stream = patch_head_input_stream; } // Fill PNG png->width = width; png->height = height; png->is_opaque = is_opaque; png->buffer = buffer; png->apng = true; png->buffer_index = -1; png->frame_info_array = frame_info_array; png->frame_count = frame_count; png->backup = NULL; // Render first frame PNG_advance(png); } else { read_image(png_ptr, buffer, width, height); // End read png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // Close input stream close_patch_head_input_stream(env, patch_head_input_stream); destroy_patch_head_input_stream(env, &patch_head_input_stream); // Fill PNG png->width = width; png->height = height; png->buffer = buffer; png->apng = false; png->buffer_index = 0; png->frame_info_array = NULL; png->frame_count = 0; png->backup = NULL; png->partially = false; png->png_ptr = NULL; png->info_ptr = NULL; png->patch_head_input_stream = NULL; } return png; }