mirror of
https://github.com/minetest/irrlicht.git
synced 2024-12-27 19:20:31 +01:00
9954667c45
The exact commands to make this commit were: git reset --hard origin/master find -type f | # list all regular files grep -E '\.(h|cpp|fsh|vsh|mm)|LICENSE$' | # filter for text files xargs -n 1 -P $(nproc) sed -i 's:\s*$::' # for each file, trim trailing whitespace including the CR git commit -a
214 lines
4.7 KiB
C++
214 lines
4.7 KiB
C++
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "CImageWriterJPG.h"
|
|
|
|
#include "CColorConverter.h"
|
|
#include "IWriteFile.h"
|
|
#include "CImage.h"
|
|
#include "irrString.h"
|
|
#include "os.h"
|
|
|
|
#include <stdio.h> // required for jpeglib.h
|
|
extern "C"
|
|
{
|
|
#include <jpeglib.h>
|
|
#include <jerror.h>
|
|
}
|
|
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
// The writer uses a 4k buffer and flushes to disk each time it's filled
|
|
#define OUTPUT_BUF_SIZE 4096
|
|
typedef struct
|
|
{
|
|
struct jpeg_destination_mgr pub;/* public fields */
|
|
|
|
io::IWriteFile* file; /* target file */
|
|
JOCTET buffer[OUTPUT_BUF_SIZE]; /* image buffer */
|
|
} mem_destination_mgr;
|
|
|
|
|
|
typedef mem_destination_mgr * mem_dest_ptr;
|
|
|
|
|
|
// init
|
|
static void jpeg_init_destination(j_compress_ptr cinfo)
|
|
{
|
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
|
dest->pub.next_output_byte = dest->buffer;
|
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
|
}
|
|
|
|
|
|
// flush to disk and reset buffer
|
|
static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
|
|
{
|
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
|
|
|
// for now just exit upon file error
|
|
if (dest->file->write(dest->buffer, OUTPUT_BUF_SIZE) != OUTPUT_BUF_SIZE)
|
|
ERREXIT (cinfo, JERR_FILE_WRITE);
|
|
|
|
dest->pub.next_output_byte = dest->buffer;
|
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void jpeg_term_destination(j_compress_ptr cinfo)
|
|
{
|
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
|
const size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
|
// for now just exit upon file error
|
|
if (dest->file->write(dest->buffer, datacount) != datacount)
|
|
ERREXIT (cinfo, JERR_FILE_WRITE);
|
|
}
|
|
|
|
|
|
// set up buffer data
|
|
static void jpeg_file_dest(j_compress_ptr cinfo, io::IWriteFile* file)
|
|
{
|
|
if (cinfo->dest == NULL)
|
|
{ /* first time for this JPEG object? */
|
|
cinfo->dest = (struct jpeg_destination_mgr *)
|
|
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
|
|
JPOOL_PERMANENT,
|
|
sizeof(mem_destination_mgr));
|
|
}
|
|
|
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; /* for casting */
|
|
|
|
/* Initialize method pointers */
|
|
dest->pub.init_destination = jpeg_init_destination;
|
|
dest->pub.empty_output_buffer = jpeg_empty_output_buffer;
|
|
dest->pub.term_destination = jpeg_term_destination;
|
|
|
|
/* Initialize private member */
|
|
dest->file = file;
|
|
}
|
|
|
|
|
|
/* write_JPEG_memory: store JPEG compressed image into memory.
|
|
*/
|
|
static bool writeJPEGFile(io::IWriteFile* file, IImage* image, u32 quality)
|
|
{
|
|
void (*format)(const void*, s32, void*) = 0;
|
|
switch( image->getColorFormat () )
|
|
{
|
|
case ECF_R8G8B8:
|
|
format = CColorConverter::convert_R8G8B8toR8G8B8;
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
format = CColorConverter::convert_A8R8G8B8toR8G8B8;
|
|
break;
|
|
case ECF_A1R5G5B5:
|
|
format = CColorConverter::convert_A1R5G5B5toB8G8R8;
|
|
break;
|
|
case ECF_R5G6B5:
|
|
format = CColorConverter::convert_R5G6B5toR8G8B8;
|
|
break;
|
|
default:
|
|
os::Printer::log("writeJPEGFile does not support image format", ColorFormatNames[image->getColorFormat()], ELL_WARNING);
|
|
break;
|
|
}
|
|
|
|
// couldn't find a color converter
|
|
if ( 0 == format )
|
|
return false;
|
|
|
|
const core::dimension2du dim = image->getDimension();
|
|
|
|
struct jpeg_compress_struct cinfo;
|
|
struct jpeg_error_mgr jerr;
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
jpeg_create_compress(&cinfo);
|
|
jpeg_file_dest(&cinfo, file);
|
|
cinfo.image_width = dim.Width;
|
|
cinfo.image_height = dim.Height;
|
|
cinfo.input_components = 3;
|
|
cinfo.in_color_space = JCS_RGB;
|
|
|
|
jpeg_set_defaults(&cinfo);
|
|
|
|
if ( 0 == quality )
|
|
quality = 75;
|
|
|
|
jpeg_set_quality(&cinfo, quality, TRUE);
|
|
jpeg_start_compress(&cinfo, TRUE);
|
|
|
|
u8 * dest = new u8[dim.Width*3];
|
|
|
|
if (dest)
|
|
{
|
|
const u32 pitch = image->getPitch();
|
|
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
|
|
row_pointer[0] = dest;
|
|
|
|
u8* src = (u8*)image->getData();
|
|
|
|
while (cinfo.next_scanline < cinfo.image_height)
|
|
{
|
|
// convert next line
|
|
format( src, dim.Width, dest );
|
|
src += pitch;
|
|
jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
|
}
|
|
|
|
delete [] dest;
|
|
|
|
/* Step 6: Finish compression */
|
|
jpeg_finish_compress(&cinfo);
|
|
}
|
|
|
|
/* Step 7: Destroy */
|
|
jpeg_destroy_compress(&cinfo);
|
|
|
|
return (dest != 0);
|
|
}
|
|
|
|
|
|
} // namespace video
|
|
} // namespace irr
|
|
|
|
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
IImageWriter* createImageWriterJPG()
|
|
{
|
|
return new CImageWriterJPG;
|
|
}
|
|
|
|
CImageWriterJPG::CImageWriterJPG()
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CImageWriterJPG");
|
|
#endif
|
|
}
|
|
|
|
|
|
bool CImageWriterJPG::isAWriteableFileExtension(const io::path& filename) const
|
|
{
|
|
return core::hasFileExtension ( filename, "jpg", "jpeg" );
|
|
}
|
|
|
|
|
|
bool CImageWriterJPG::writeImage(io::IWriteFile *file, IImage *image, u32 quality) const
|
|
{
|
|
return writeJPEGFile(file, image, quality);
|
|
}
|
|
|
|
} // namespace video
|
|
} // namespace irr
|