// split out of png_decode to simplify resource cleanup and avoid // "dtor / setjmp interaction" warning. static Status png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, Tex* t) { png_set_read_fn(png_ptr, da, io_read); // read header and determine format png_read_info(png_ptr, info_ptr); png_uint_32 w, h; int bit_depth, colour_type; png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &colour_type, 0, 0, 0); const size_t pitch = png_get_rowbytes(png_ptr, info_ptr); const u32 bpp = (u32)(pitch/w * 8); size_t flags = 0; if(bpp == 32) flags |= TEX_ALPHA; if(colour_type == PNG_COLOR_TYPE_GRAY) flags |= TEX_GREY; // make sure format is acceptable if(bit_depth != 8) WARN_RETURN(ERR::TEX_NOT_8BIT_PRECISION); if(colour_type & PNG_COLOR_MASK_PALETTE) WARN_RETURN(ERR::TEX_INVALID_COLOR_TYPE); const size_t img_size = pitch * h; shared_ptr<u8> data; AllocateAligned(data, img_size, pageSize); std::vector<RowPtr> rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0); png_read_image(png_ptr, (png_bytepp)&rows[0]); png_read_end(png_ptr, info_ptr); // success; make sure all data was consumed. ENSURE(da->pos == da->cur_size); // store image info t->data = data; t->dataSize = img_size; t->ofs = 0; t->w = w; t->h = h; t->bpp = bpp; t->flags = flags; return INFO::OK; }
static Status jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, DynArray* da) { dst_prepare(cinfo, da); // describe image format // required: cinfo->image_width = (JDIMENSION)t->m_Width; cinfo->image_height = (JDIMENSION)t->m_Height; cinfo->input_components = (int)t->m_Bpp / 8; cinfo->in_color_space = (t->m_Bpp == 8)? JCS_GRAYSCALE : JCS_RGB; // defaults depend on cinfo->in_color_space already having been set! jpeg_set_defaults(cinfo); // (add optional settings, e.g. quality, here) // TRUE ensures that we will write a complete interchange-JPEG file. // don't change unless you are very sure of what you're doing. jpeg_start_compress(cinfo, TRUE); // if BGR, convert to RGB. WARN_IF_ERR(t->transform_to(t->m_Flags & ~TEX_BGR)); const size_t pitch = t->m_Width * t->m_Bpp / 8; u8* data = t->get_data(); std::vector<RowPtr> rows = tex_codec_alloc_rows(data, t->m_Height, pitch, t->m_Flags, TEX_TOP_DOWN); // could use cinfo->output_scanline to keep track of progress, // but we need to count lines_left anyway (paranoia). JSAMPARRAY row = (JSAMPARRAY)&rows[0]; JDIMENSION lines_left = (JDIMENSION)t->m_Height; while(lines_left != 0) { JDIMENSION lines_read = jpeg_write_scanlines(cinfo, row, lines_left); row += lines_read; lines_left -= lines_read; // we've decoded in-place; no need to further process } jpeg_finish_compress(cinfo); Status ret = INFO::OK; if(cinfo->err->num_warnings != 0) ret = WARN::TEX_INVALID_DATA; return ret; }
// split out of png_encode to simplify resource cleanup and avoid // "dtor / setjmp interaction" warning. static Status png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da) { const png_uint_32 w = (png_uint_32)t->w, h = (png_uint_32)t->h; const size_t pitch = w * t->bpp / 8; int colour_type; switch(t->flags & (TEX_GREY|TEX_ALPHA)) { case TEX_GREY|TEX_ALPHA: colour_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case TEX_GREY: colour_type = PNG_COLOR_TYPE_GRAY; break; case TEX_ALPHA: colour_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: colour_type = PNG_COLOR_TYPE_RGB; break; } png_set_write_fn(png_ptr, da, io_write, io_flush); png_set_IHDR(png_ptr, info_ptr, w, h, 8, colour_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); u8* data = tex_get_data(t); std::vector<RowPtr> rows = tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN); // PNG is native RGB. const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY; png_set_rows(png_ptr, info_ptr, (png_bytepp)&rows[0]); png_write_png(png_ptr, info_ptr, png_transforms, 0); return INFO::OK; }
static Status jpg_decode_impl(rpU8 data, size_t size, jpeg_decompress_struct* cinfo, Tex* t) { src_prepare(cinfo, data, size); // ignore return value since: // - suspension is not possible with the mem data source // - we passed TRUE to raise an error if table-only JPEG file (void)jpeg_read_header(cinfo, TRUE); // set libjpg output format. we cannot go with the default because // Photoshop writes non-standard CMYK files that must be converted to RGB. size_t flags = 0; cinfo->out_color_space = JCS_RGB; if(cinfo->num_components == 1) { flags |= TEX_GREY; cinfo->out_color_space = JCS_GRAYSCALE; } // lower quality, but faster cinfo->dct_method = JDCT_IFAST; cinfo->do_fancy_upsampling = FALSE; // ignore return value since suspension is not possible with the // mem data source. // note: since we've set out_color_space, JPEG will always // return an acceptable image format; no need to check. (void)jpeg_start_decompress(cinfo); // scaled output image dimensions and final bpp are now available. int w = cinfo->output_width; int h = cinfo->output_height; int bpp = cinfo->output_components * 8; // alloc destination buffer const size_t pitch = w * bpp / 8; const size_t imgSize = pitch * h; // for allow_rows shared_ptr<u8> img; AllocateAligned(img, imgSize, pageSize); // read rows std::vector<RowPtr> rows = tex_codec_alloc_rows(img.get(), h, pitch, TEX_TOP_DOWN, 0); // could use cinfo->output_scanline to keep track of progress, // but we need to count lines_left anyway (paranoia). JSAMPARRAY row = (JSAMPARRAY)&rows[0]; JDIMENSION lines_left = h; while(lines_left != 0) { JDIMENSION lines_read = jpeg_read_scanlines(cinfo, row, lines_left); row += lines_read; lines_left -= lines_read; // we've decoded in-place; no need to further process } // ignore return value since suspension is not possible with the // mem data source. (void)jpeg_finish_decompress(cinfo); Status ret = INFO::OK; if(cinfo->err->num_warnings != 0) ret = WARN::TEX_INVALID_DATA; // store image info and validate return ret | t->wrap(w,h,bpp,flags,img,0); }