Skip to content

Commit

Permalink
Merge pull request #40 from mgehre/jpeg
Browse files Browse the repository at this point in the history
JPEG decoding and bitmap fill rendering
  • Loading branch information
alexp-sssup committed May 15, 2011
2 parents 1b32a11 + a200532 commit 7add25c
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 38 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Expand Up @@ -150,6 +150,7 @@ INCLUDE(FindOpenGL REQUIRED)
INCLUDE(FindPCRE REQUIRED)
INCLUDE(FindFTGL REQUIRED)
INCLUDE(FindGLEW REQUIRED)
INCLUDE(FindJPEG REQUIRED)
IF(UNIX)
INCLUDE(FindPkgConfig REQUIRED)
pkg_check_modules(XMLPP REQUIRED libxml++-2.6>=2.33.1)
Expand Down Expand Up @@ -215,6 +216,7 @@ INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${PCRE_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${FTGL_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${JPEG_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${XMLPP_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${EXTRA_LIBS_INCLUDE_DIRS})

Expand Down
3 changes: 2 additions & 1 deletion src/CMakeLists.txt
Expand Up @@ -35,6 +35,7 @@ SET(LIBSPARK_SOURCES
backends/extscriptobject.cpp
backends/geometry.cpp
backends/graphics.cpp
backends/image.cpp
backends/input.cpp
backends/netutils.cpp
backends/pluginmanager.cpp
Expand Down Expand Up @@ -98,7 +99,7 @@ ENDIF (CMAKE_COMPILER_IS_GNUCC)

TARGET_LINK_LIBRARIES(spark ${EXTRA_LIBS_LIBRARIES} ${ZLIB_LIBRARIES}
${Boost_LIBRARIES} ${LLVM_LIBS_CORE} ${LLVM_LIBS_JIT} ${SDL_LIBRARY}
${OPTIONAL_LIBRARIES} ${GTK_LIBRARIES} ${FREETYPE_LIBRARIES}
${OPTIONAL_LIBRARIES} ${GTK_LIBRARIES} ${FREETYPE_LIBRARIES} ${JPEG_LIBRARIES}
${OPENGL_LIBRARIES} ${FTGL_LIBRARIES} ${GLEW_LIBRARIES} ${PCRE_LIBRARIES}
${Threads_LIBRARIES} ${XMLPP_LIBRARIES} ${CMAKE_DL_LIBS})
SET_TARGET_PROPERTIES(spark PROPERTIES VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
Expand Down
75 changes: 51 additions & 24 deletions src/backends/graphics.cpp
Expand Up @@ -550,30 +550,36 @@ cairo_pattern_t* CairoRenderer::FILLSTYLEToCairo(const FILLSTYLE& style, double
if(style.bitmap==NULL)
throw RunTimeException("Invalid bitmap");

// IntSize size = style.bitmap->getBitmapSize();
// cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width, size.height);
// uint8_t* tmp = cairo_image_surface_get_data(surface);
// for(uint32_t i=0;i<size.width*size.height;i++)
// {
// tmp[i*4+1]=i;
// tmp[i*4+3]=0xff;
// }

// cairo_set_source_surface(cr, surface, 0, 0);
// pattern = cairo_get_source(cr);
// if (style.FillStyleType == NON_SMOOTHED_REPEATING_BITMAP ||
// style.FillStyleType == REPEATING_BITMAP)
// cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
// else
// cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);

// if (style.FillStyleType == NON_SMOOTHED_REPEATING_BITMAP ||
// style.FillStyle == NON_SMOOTHED_CLIPPED_BITMAP)
// cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
// else
// cairo_pattern_set_filter(pattern, CAIRO_FILTER_BILINEAR);

// bitmaps not implemented, fall through
IntSize size = style.bitmap->getBitmapSize();
//TODO: ARGB32 always give a white surface
cairo_surface_t* surface = cairo_image_surface_create_for_data (style.bitmap->data,
CAIRO_FORMAT_RGB24, size.width, size.height,
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, size.width));

pattern = cairo_pattern_create_for_surface(surface);
cairo_surface_destroy(surface);

cairo_matrix_t mat = MATRIXToCairo(style.Matrix);
cairo_status_t st = cairo_matrix_invert(&mat);
assert(st == CAIRO_STATUS_SUCCESS);
mat.x0 /= scaleCorrection;
mat.y0 /= scaleCorrection;

cairo_pattern_set_matrix (pattern, &mat);
assert(cairo_pattern_status(pattern) == CAIRO_STATUS_SUCCESS);

if(style.FillStyleType == NON_SMOOTHED_REPEATING_BITMAP ||
style.FillStyleType == REPEATING_BITMAP)
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
else
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);

if(style.FillStyleType == NON_SMOOTHED_REPEATING_BITMAP ||
style.FillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)
cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
else
cairo_pattern_set_filter(pattern, CAIRO_FILTER_BILINEAR);
break;
}
default:
LOG(LOG_NOT_IMPLEMENTED, "Unsupported fill style " << (int)style.FillStyleType);
Expand Down Expand Up @@ -791,3 +797,24 @@ bool CairoRenderer::hitTest(const std::vector<GeomToken>& tokens, float scaleFac
cairo_surface_destroy(cairoSurface);
return ret;
}

uint8_t* CairoRenderer::convertBitmapToCairo(uint8_t* inData, uint32_t width, uint32_t height)
{
uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width);
uint8_t* outData = new uint8_t[stride * height];
for(uint32_t i = 0; i < height; i++)
{
for(uint32_t j = 0; j < width; j++)
{
uint32_t* outDataPos = (uint32_t*)(outData+i*stride) + j;
uint32_t pdata = 0;
/* the alpha channel is set to zero above */
uint8_t* rgbData = ((uint8_t*)&pdata)+1;
/* copy the RGB bytes to rgbData */
memcpy(rgbData, inData+(i*width+j)*3, 3);
/* cairo needs this in host endianess */
*outDataPos = BigEndianToHost32(pdata);
}
}
return outData;
}
6 changes: 6 additions & 0 deletions src/backends/graphics.h
Expand Up @@ -271,6 +271,12 @@ class CairoRenderer: public ITextureUploadable, public IThreadJob
void threadAbort();
void jobFence();
void execute();

/*
* Converts data (which is in RGB format) to the format internally used by cairo.
* This function new[]'s the returned value, which has to be freed by the caller.
*/
static uint8_t* convertBitmapToCairo(uint8_t* data, uint32_t width, uint32_t height);
};

};
Expand Down
130 changes: 130 additions & 0 deletions src/backends/image.cpp
@@ -0,0 +1,130 @@
/**************************************************************************
Lightspark, a free flash player implementation
Copyright (C) 2011 Matthias Gehre (M.Gehre@gmx.de)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#include <stdio.h>
#include <string.h>

#include "logger.h"

extern "C" {
#include "jpeglib.h"
#include "jerror.h"
}

#include <setjmp.h>
#include "image.h"

namespace lightspark
{

struct source_mgr : public jpeg_source_mgr
{
source_mgr(uint8_t* _data, int _len) : data(_data), len(_len) {}
uint8_t* data;
int len;
};

static void init_source(j_decompress_ptr cinfo) {
source_mgr* src = (source_mgr*)cinfo->src;
src->next_input_byte = (const JOCTET*)src->data;
src->bytes_in_buffer = src->len;
}

static boolean fill_input_buffer(j_decompress_ptr cinfo) {
return FALSE;
}

static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
source_mgr* src = (source_mgr*)cinfo->src;
src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
src->bytes_in_buffer -= num_bytes;
}

static boolean resync_to_restart(j_decompress_ptr cinfo, int desired) {
return TRUE;
}

static void term_source(j_decompress_ptr /*cinfo*/) {}

struct error_mgr : jpeg_error_mgr {
jmp_buf jmpBuf;
};

void error_exit(j_common_ptr cinfo) {
error_mgr* error = (error_mgr*)cinfo->err;
(*error->output_message) (cinfo);
/* Let the memory manager delete any temp files before we die */
jpeg_destroy(cinfo);
longjmp(error->jmpBuf, -1);
}

uint8_t* ImageDecoder::decodeJPEG(uint8_t* inData, int len, uint32_t* width, uint32_t* height)
{
struct jpeg_decompress_struct cinfo;
struct error_mgr err;
struct source_mgr src(inData,len);

src.init_source = init_source;
src.fill_input_buffer = fill_input_buffer;
src.skip_input_data = skip_input_data;
src.resync_to_restart = resync_to_restart;
src.term_source = term_source;

cinfo.err = jpeg_std_error(&err);
err.error_exit = error_exit;

if (setjmp(err.jmpBuf)) {
return NULL;
}

jpeg_create_decompress(&cinfo);
cinfo.src = &src;
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);

*width = cinfo.output_width;
*height = cinfo.output_height;
if(cinfo.num_components != 3)
{
LOG(LOG_NOT_IMPLEMENTED,"Only RGB JPEG's are supported");
/* TODO: is this the right thing for aborting? */
jpeg_abort_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return NULL;
}

int rowstride = cinfo.output_width * cinfo.output_components;
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowstride, 1);

uint8_t* outData = new uint8_t[cinfo.output_height * rowstride];

/* read one scanline at a time */
int y=0;
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
memcpy(&outData[y*rowstride], buffer[0], rowstride);
y++;
}

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);

return outData;
}

}
34 changes: 34 additions & 0 deletions src/backends/image.h
@@ -0,0 +1,34 @@
/**************************************************************************
Lightspark, a free flash player implementation
Copyright (C) 2011 Matthias Gehre (M.Gehre@gmx.de)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#include <stdint.h>

namespace lightspark
{

class ImageDecoder
{
public:
/*
* Returns a new[]'ed array of decompressed data and sets width, height and format
* Return NULL on error
*/
static uint8_t* decodeJPEG(uint8_t* inData, int len, uint32_t* width, uint32_t* height);
};

}
21 changes: 12 additions & 9 deletions src/parsing/tags.cpp
Expand Up @@ -1501,13 +1501,11 @@ DefineBitsJPEG2Tag::DefineBitsJPEG2Tag(RECORDHEADER h, std::istream& in):Diction
in >> CharacterId;
//Read image data
int dataSize=Header.getLength()-2;
data=new(nothrow) uint8_t[dataSize];
in.read((char*)data,dataSize);
}
uint8_t* inData=new(nothrow) uint8_t[dataSize];
in.read((char*)inData,dataSize);

DefineBitsJPEG2Tag::~DefineBitsJPEG2Tag()
{
delete[] data;
Bitmap::fromJPEG(inData,dataSize);
delete[] inData;
}

DefineBitsJPEG3Tag::DefineBitsJPEG3Tag(RECORDHEADER h, std::istream& in):DictionaryTag(h),alphaData(NULL)
Expand All @@ -1516,20 +1514,25 @@ DefineBitsJPEG3Tag::DefineBitsJPEG3Tag(RECORDHEADER h, std::istream& in):Diction
UI32_SWF dataSize;
in >> CharacterId >> dataSize;
//Read image data
data=new(nothrow) uint8_t[dataSize];
in.read((char*)data,dataSize);
uint8_t* inData=new(nothrow) uint8_t[dataSize];
in.read((char*)inData,dataSize);

//TODO: check header. Could also be PNG or GIF
Bitmap::fromJPEG(inData,dataSize);
delete[] inData;

//Read alpha data (if any)
int alphaSize=Header.getLength()-dataSize-6;
if(alphaSize>0) //If less that 0 the consistency check on tag size will stop later
{
LOG(LOG_NOT_IMPLEMENTED,"DefineBitsJPEG3Tag does not use alpha yet");
alphaData=new(nothrow) uint8_t[alphaSize];
in.read((char*)alphaData,alphaSize);
}
}

DefineBitsJPEG3Tag::~DefineBitsJPEG3Tag()
{
delete[] data;
delete[] alphaData;
}

Expand Down
3 changes: 0 additions & 3 deletions src/parsing/tags.h
Expand Up @@ -581,18 +581,15 @@ class DefineBitsJPEG2Tag: public DictionaryTag, public Bitmap
{
private:
UI16_SWF CharacterId;
uint8_t* data;
public:
DefineBitsJPEG2Tag(RECORDHEADER h, std::istream& in);
~DefineBitsJPEG2Tag();
int getId(){ return CharacterId; }
};

class DefineBitsJPEG3Tag: public DictionaryTag, public Bitmap
{
private:
UI16_SWF CharacterId;
uint8_t* data;
uint8_t* alphaData;
public:
DefineBitsJPEG3Tag(RECORDHEADER h, std::istream& in);
Expand Down
17 changes: 16 additions & 1 deletion src/scripting/flashdisplay.cpp
Expand Up @@ -31,6 +31,7 @@
#include "class.h"
#include "backends/rendering.h"
#include "backends/geometry.h"
#include "backends/image.h"
#include "compat.h"

#include <GL/glew.h>
Expand Down Expand Up @@ -3071,7 +3072,21 @@ bool Bitmap::getBounds(number_t& xmin, number_t& xmax, number_t& ymin, number_t&

IntSize Bitmap::getBitmapSize() const
{
return IntSize(100,100);
return size;
}

bool Bitmap::fromJPEG(uint8_t *inData, int len)
{
assert(!data);
uint8_t* rgbData = ImageDecoder::decodeJPEG(inData, len, &size.width, &size.height);
data = CairoRenderer::convertBitmapToCairo(rgbData, size.width, size.height);
delete[] rgbData;
if(!data)
{
LOG(LOG_ERROR, "Error decoding jpeg");
return false;
}
return true;
}

void SimpleButton::sinit(Class_base* c)
Expand Down

0 comments on commit 7add25c

Please sign in to comment.