Initial commit
This commit is contained in:
2649
engines/sword25/gfx/image/art.cpp
Normal file
2649
engines/sword25/gfx/image/art.cpp
Normal file
File diff suppressed because it is too large
Load Diff
215
engines/sword25/gfx/image/art.h
Normal file
215
engines/sword25/gfx/image/art.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Libart_LGPL - library of basic graphic primitives
|
||||
*
|
||||
* Copyright (c) 1998 Raph Levien
|
||||
*
|
||||
* Licensed under GNU LGPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/* Simple macros to set up storage allocation and basic types for libart
|
||||
functions. */
|
||||
|
||||
#ifndef __ART_H__
|
||||
#define __ART_H__
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/* These aren't, strictly speaking, configuration macros, but they're
|
||||
damn handy to have around, and may be worth playing with for
|
||||
debugging. */
|
||||
#define art_new(type, n) ((type *)malloc ((n) * sizeof(type)))
|
||||
|
||||
#define art_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
|
||||
|
||||
/* This one must be used carefully - in particular, p and max should
|
||||
be variables. They can also be pstruct->el lvalues. */
|
||||
#define art_expand(p, type, max) \
|
||||
do { \
|
||||
if (max) {\
|
||||
type *tmp = art_renew(p, type, max <<= 1); \
|
||||
if (!tmp) error("Cannot reallocate memory for art data"); \
|
||||
p = tmp; \
|
||||
} else { \
|
||||
max = 1; \
|
||||
p = art_new(type, 1); \
|
||||
if (!p) error("Cannot allocate memory for art data"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct ArtDRect {
|
||||
/*< public >*/
|
||||
double x0, y0, x1, y1;
|
||||
};
|
||||
|
||||
struct ArtPoint {
|
||||
/*< public >*/
|
||||
double x, y;
|
||||
};
|
||||
|
||||
/* Basic data structures and constructors for sorted vector paths */
|
||||
|
||||
struct ArtSVPSeg {
|
||||
int n_points;
|
||||
int dir; /* == 0 for "up", 1 for "down" */
|
||||
ArtDRect bbox;
|
||||
ArtPoint *points;
|
||||
};
|
||||
|
||||
struct ArtSVP {
|
||||
int n_segs;
|
||||
ArtSVPSeg segs[1];
|
||||
};
|
||||
|
||||
void art_svp_free(ArtSVP *svp);
|
||||
|
||||
/* Basic data structures and constructors for bezier paths */
|
||||
|
||||
enum ArtPathcode {
|
||||
ART_MOVETO,
|
||||
ART_MOVETO_OPEN,
|
||||
ART_CURVETO,
|
||||
ART_LINETO,
|
||||
ART_END
|
||||
};
|
||||
|
||||
struct ArtBpath {
|
||||
/*< public >*/
|
||||
ArtPathcode code;
|
||||
double x1;
|
||||
double y1;
|
||||
double x2;
|
||||
double y2;
|
||||
double x3;
|
||||
double y3;
|
||||
};
|
||||
|
||||
/* Basic data structures and constructors for simple vector paths */
|
||||
|
||||
/* CURVETO is not allowed! */
|
||||
struct ArtVpath {
|
||||
ArtPathcode code;
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
/* Some of the functions need to go into their own modules */
|
||||
|
||||
void art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
|
||||
ArtPathcode code, double x, double y);
|
||||
|
||||
ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness);
|
||||
|
||||
/* The funky new SVP intersector. */
|
||||
|
||||
#ifndef ART_WIND_RULE_DEFINED
|
||||
#define ART_WIND_RULE_DEFINED
|
||||
enum ArtWindRule {
|
||||
ART_WIND_RULE_NONZERO,
|
||||
ART_WIND_RULE_INTERSECT,
|
||||
ART_WIND_RULE_ODDEVEN,
|
||||
ART_WIND_RULE_POSITIVE
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ArtSvpWriter {
|
||||
int (*add_segment)(ArtSvpWriter *self, int wind_left, int delta_wind,
|
||||
double x, double y);
|
||||
void (*add_point)(ArtSvpWriter *self, int seg_id, double x, double y);
|
||||
void (*close_segment)(ArtSvpWriter *self, int seg_id);
|
||||
};
|
||||
|
||||
ArtSvpWriter *art_svp_writer_rewind_new(ArtWindRule rule);
|
||||
|
||||
ArtSVP *art_svp_writer_rewind_reap(ArtSvpWriter *self);
|
||||
|
||||
int art_svp_seg_compare(const void *s1, const void *s2);
|
||||
|
||||
void art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out);
|
||||
|
||||
|
||||
/* Sort vector paths into sorted vector paths. */
|
||||
|
||||
ArtSVP *art_svp_from_vpath(ArtVpath *vpath);
|
||||
|
||||
/* Sort vector paths into sorted vector paths. */
|
||||
|
||||
enum ArtPathStrokeJoinType {
|
||||
ART_PATH_STROKE_JOIN_MITER,
|
||||
ART_PATH_STROKE_JOIN_ROUND,
|
||||
ART_PATH_STROKE_JOIN_BEVEL
|
||||
};
|
||||
|
||||
enum ArtPathStrokeCapType {
|
||||
ART_PATH_STROKE_CAP_BUTT,
|
||||
ART_PATH_STROKE_CAP_ROUND,
|
||||
ART_PATH_STROKE_CAP_SQUARE
|
||||
};
|
||||
|
||||
ArtSVP *art_svp_vpath_stroke(ArtVpath *vpath,
|
||||
ArtPathStrokeJoinType join,
|
||||
ArtPathStrokeCapType cap,
|
||||
double line_width,
|
||||
double miter_limit,
|
||||
double flatness);
|
||||
|
||||
/* This version may have winding numbers exceeding 1. */
|
||||
ArtVpath *art_svp_vpath_stroke_raw(ArtVpath *vpath,
|
||||
ArtPathStrokeJoinType join,
|
||||
ArtPathStrokeCapType cap,
|
||||
double line_width,
|
||||
double miter_limit,
|
||||
double flatness);
|
||||
|
||||
|
||||
/* The spiffy antialiased renderer for sorted vector paths. */
|
||||
|
||||
struct ArtSVPRenderAAStep {
|
||||
int x;
|
||||
int delta; /* stored with 16 fractional bits */
|
||||
};
|
||||
|
||||
struct ArtSVPRenderAAIter;
|
||||
|
||||
ArtSVPRenderAAIter *art_svp_render_aa_iter(const ArtSVP *svp,
|
||||
int x0, int y0, int x1, int y1);
|
||||
|
||||
void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
|
||||
ArtSVPRenderAAStep **p_steps, int *p_n_steps);
|
||||
|
||||
void art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter);
|
||||
|
||||
void art_svp_render_aa(const ArtSVP *svp,
|
||||
int x0, int y0, int x1, int y1,
|
||||
void (*callback)(void *callback_data,
|
||||
int y,
|
||||
int start,
|
||||
ArtSVPRenderAAStep *steps, int n_steps),
|
||||
void *callback_data);
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif /* __ART_H__ */
|
||||
193
engines/sword25/gfx/image/image.h
Normal file
193
engines/sword25/gfx/image/image.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
BS_Image
|
||||
--------
|
||||
|
||||
Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_IMAGE_H
|
||||
#define SWORD25_IMAGE_H
|
||||
|
||||
// Includes
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "common/rect.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class RectangleList;
|
||||
|
||||
class Image {
|
||||
public:
|
||||
virtual ~Image() {}
|
||||
|
||||
//@{
|
||||
/** @name Accessor methods */
|
||||
|
||||
/**
|
||||
@brief Returns the width of the image in pixels
|
||||
*/
|
||||
virtual int getWidth() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns the height of the image in pixels
|
||||
*/
|
||||
virtual int getHeight() const = 0;
|
||||
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/** @name Render methodes */
|
||||
|
||||
/**
|
||||
@brief renders the image in the framebuffer
|
||||
@param pDest a pointer to the target image. In most cases this is the framebuffer.
|
||||
@param PosX the position on the X-axis in the target image in pixels where the image is supposed to be rendered.<br>
|
||||
The default value is 0.
|
||||
@param PosY the position on the Y-axis in the target image in pixels where the image is supposed to be rendered.<br>
|
||||
The default value is 0.
|
||||
@param Flipping how the image should be flipped.<br>
|
||||
The default value is Graphics::FLIP_NONE (no flipping)
|
||||
@param pSrcPartRect Pointer on Common::Rect which specifies the section to be rendered. If the whole image has to be rendered the Pointer is NULL.<br>
|
||||
This referes to the unflipped and unscaled image.<br>
|
||||
The default value is NULL.
|
||||
@param Color an ARGB color value, which determines the parameters for the color modulation und alpha blending.<br>
|
||||
The alpha component of the color determines the alpha blending parameter (0 = no covering, 255 = full covering).<br>
|
||||
The color components determines the color for color modulation.<br>
|
||||
The default value is BS_ARGB(255, 255, 255, 255) (full covering, no color modulation).
|
||||
The macros BS_RGB and BS_ARGB can be used for the creation of the color value.
|
||||
@param Width the output width of the screen section.
|
||||
The images will be scaled if the output width of the screen section differs from the image section.<br>
|
||||
The value -1 determines that the image should not be scaled.<br>
|
||||
The default value is -1.
|
||||
@param Width the output height of the screen section.
|
||||
The images will be scaled if the output width of the screen section differs from the image section.<br>
|
||||
The value -1 determines that the image should not be scaled.<br>
|
||||
The default value is -1.
|
||||
@return returns false if the rendering failed.
|
||||
@remark Not all blitting operations of all BS_Image classes are supported.<br>
|
||||
More information can be find in the class description of BS_Image and the following methodes:
|
||||
- IsBlitTarget()
|
||||
- IsScalingAllowed()
|
||||
- IsFillingAllowed()
|
||||
- IsAlphaAllowed()
|
||||
- IsColorModulationAllowed()
|
||||
- IsSetContentAllowed()
|
||||
*/
|
||||
virtual bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) = 0;
|
||||
|
||||
/**
|
||||
@brief fills a rectangular section of the image with a color.
|
||||
@param pFillRect Pointer on Common::Rect which specifies the section of the image which is supposed to be filled. If the whole image has to be filled this value is NULL.<br>
|
||||
The default value is NULL.
|
||||
@param Color the 32 Bit color value for filling the image section.
|
||||
@remark It is possible to create transparent rectangulars by using a color with alpha value not equal to 255.
|
||||
@remark Independent from the color format of the image, it must be given a 32 bit color value. The macros BS_RGB and BS_ARGB can be used for the creation of the color value.
|
||||
@remark If the rectangular is not completely inside the screen area, it will be automatically trimmed.
|
||||
*/
|
||||
virtual bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) = 0;
|
||||
|
||||
/**
|
||||
@brief Fills the content of the image with pixel data.
|
||||
@param Pixeldata a vector which cotains the pixel data. They must be present in the color format of the image and there must be enough data available for filling the whole image.
|
||||
@param Offset the offset in Byte in Pixeldata-Vector on which the first pixel to write is located.<br>
|
||||
The default value is 0.
|
||||
@param Stride the distance in Byte between the end of line and the beginning of a new line in Pixeldata-Vector.<br>
|
||||
The default value is 0.
|
||||
@return returns false, if the call failed.
|
||||
@remark A call of this methode is only allowd if IsSetContentAllowed() returns true.
|
||||
*/
|
||||
virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) = 0;
|
||||
|
||||
/**
|
||||
@brief Reads out a pixel of the image.
|
||||
@param X the X-coordinate of the pixel.
|
||||
@param Y the y-coordinate of the pixel.
|
||||
@return Returns the 32-bit color value of the pixel at the given position.
|
||||
@remark This methode should not be used in no way to read out bigger parts of the image because the method is very slow. The method is rather intended for reading out single pixels of the image..
|
||||
*/
|
||||
virtual uint getPixel(int x, int y) = 0;
|
||||
|
||||
//@{
|
||||
/** @name Information methodes */
|
||||
|
||||
/**
|
||||
@brief Checks, if it is allowed to call BS_Image Blit().
|
||||
@return Returns false, if a Blit() call is not allowed at this object.
|
||||
*/
|
||||
virtual bool isBlitSource() const = 0;
|
||||
|
||||
/**
|
||||
@brief Checks, if the BS_Image can be a target image for a Blit call.
|
||||
@return Returns false, if a Blit() call with this object as a target is not allowed.
|
||||
*/
|
||||
virtual bool isBlitTarget() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the BS_Image is allowed to be scaled by a Blit() call.
|
||||
*/
|
||||
virtual bool isScalingAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the BS_Image is allowed to be filled by a Fill() call.
|
||||
*/
|
||||
virtual bool isFillingAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the BS_Image is allowed to be displayed with an alpha value.
|
||||
*/
|
||||
virtual bool isAlphaAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Return true, if the BS_Image is allowed to be displayed with color modulation by a Blit() call
|
||||
*/
|
||||
virtual bool isColorModulationAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the content of the BS_Image is allowed to be replaced by call of SetContent().
|
||||
*/
|
||||
virtual bool isSetContentAllowed() const = 0;
|
||||
|
||||
virtual bool isSolid() const { return false; }
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
82
engines/sword25/gfx/image/imgloader.cpp
Normal file
82
engines/sword25/gfx/image/imgloader.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/image/imgloader.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/png.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, Graphics::Surface *dest) {
|
||||
assert(dest);
|
||||
Common::MemoryReadStream *fileStr = new Common::MemoryReadStream(fileDataPtr, fileSize, DisposeAfterUse::NO);
|
||||
|
||||
::Image::PNGDecoder png;
|
||||
if (!png.loadStream(*fileStr)) // the fileStr pointer, and thus pFileData will be deleted after this is done
|
||||
error("Error while reading PNG image");
|
||||
|
||||
const Graphics::Surface *sourceSurface = png.getSurface();
|
||||
Graphics::Surface *pngSurface = sourceSurface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), png.getPalette().data(), png.getPalette().size());
|
||||
|
||||
dest->copyFrom(*pngSurface);
|
||||
|
||||
pngSurface->free();
|
||||
delete pngSurface;
|
||||
delete fileStr;
|
||||
|
||||
// Signal success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImgLoader::decodeThumbnailImage(const byte *pFileData, uint fileSize, Graphics::Surface *dest) {
|
||||
assert(dest);
|
||||
const byte *src = pFileData + 4; // skip header
|
||||
uint width = READ_LE_UINT16(src); src += 2;
|
||||
uint height = READ_LE_UINT16(src); src += 2;
|
||||
src++; // version, ignored for now
|
||||
|
||||
dest->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
|
||||
uint32 *dst = (uint32 *)dest->getBasePtr(0, 0); // treat as uint32, for pixelformat output
|
||||
byte r, g, b;
|
||||
|
||||
for (uint32 i = 0; i < width * height; i++) {
|
||||
r = *src++;
|
||||
g = *src++;
|
||||
b = *src++;
|
||||
*dst++ = dest->format.RGBToColor(r, g, b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
74
engines/sword25/gfx/image/imgloader.h
Normal file
74
engines/sword25/gfx/image/imgloader.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_IMGLOADER_H
|
||||
#define SWORD25_IMGLOADER_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
} // End of namespace Graphics
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/**
|
||||
* Class for loading PNG files, and PNG data embedded into savegames.
|
||||
*
|
||||
* Originally written by Malte Thiesen.
|
||||
*/
|
||||
class ImgLoader {
|
||||
protected:
|
||||
ImgLoader() {} // Protected constructor to prevent instances
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Decode an image.
|
||||
* @param[in] fileDatePtr pointer to the image data
|
||||
* @param[in] fileSize size of the image data in bytes
|
||||
* @param[out] dest if successful, surface will contain the image
|
||||
* data (storage is allocated via create).
|
||||
* @return false in case of an error
|
||||
*
|
||||
* @remark This function does not free the image buffer passed to it,
|
||||
* it is the callers responsibility to do so.
|
||||
*/
|
||||
static bool decodePNGImage(const byte *pFileData, uint fileSize,
|
||||
Graphics::Surface *dest);
|
||||
|
||||
static bool decodeThumbnailImage(const byte *pFileData, uint fileSize,
|
||||
Graphics::Surface *dest);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
266
engines/sword25/gfx/image/renderedimage.cpp
Normal file
266
engines/sword25/gfx/image/renderedimage.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// INCLUDES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "common/savefile.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/gfx/image/imgloader.h"
|
||||
#include "sword25/gfx/image/renderedimage.h"
|
||||
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CONSTRUCTION / DESTRUCTION
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load a NULL-terminated string from the given stream.
|
||||
*/
|
||||
static Common::String loadString(Common::SeekableReadStream &in, uint maxSize = 999) {
|
||||
Common::String result;
|
||||
|
||||
while (!in.eos() && (result.size() < maxSize)) {
|
||||
char ch = (char)in.readByte();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
result += ch;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static byte *readSavegameThumbnail(const Common::String &filename, uint &fileSize, bool &isPNG) {
|
||||
byte *pFileData;
|
||||
Common::SaveFileManager *sfm = g_system->getSavefileManager();
|
||||
Common::InSaveFile *file = sfm->openForLoading(lastPathComponent(filename, '/'));
|
||||
if (!file)
|
||||
error("Save file \"%s\" could not be loaded.", filename.c_str());
|
||||
|
||||
// Seek to the actual PNG image
|
||||
loadString(*file); // Marker (BS25SAVEGAME)
|
||||
Common::String storedVersionID = loadString(*file); // Version
|
||||
if (storedVersionID != "SCUMMVM1")
|
||||
loadString(*file);
|
||||
|
||||
loadString(*file); // Description
|
||||
uint32 compressedGamedataSize = atoi(loadString(*file).c_str());
|
||||
loadString(*file); // Uncompressed game data size
|
||||
file->skip(compressedGamedataSize); // Skip the game data and move to the thumbnail itself
|
||||
uint32 thumbnailStart = file->pos();
|
||||
|
||||
fileSize = file->size() - thumbnailStart;
|
||||
|
||||
// Check if the thumbnail is in our own format, or a PNG file.
|
||||
uint32 header = file->readUint32BE();
|
||||
isPNG = (header != MKTAG('S','C','R','N'));
|
||||
file->seek(-4, SEEK_CUR);
|
||||
|
||||
pFileData = new byte[fileSize];
|
||||
file->read(pFileData, fileSize);
|
||||
delete file;
|
||||
|
||||
return pFileData;
|
||||
}
|
||||
|
||||
RenderedImage::RenderedImage(const Common::String &filename, bool &result) :
|
||||
_alphaType(Graphics::ALPHA_FULL) {
|
||||
result = false;
|
||||
|
||||
PackageManager *pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
_backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
|
||||
// Load file
|
||||
byte *pFileData;
|
||||
uint fileSize;
|
||||
|
||||
bool isPNG = true;
|
||||
|
||||
if (filename.hasPrefix("/saves")) {
|
||||
pFileData = readSavegameThumbnail(filename, fileSize, isPNG);
|
||||
} else {
|
||||
pFileData = pPackage->getFile(filename, &fileSize);
|
||||
}
|
||||
|
||||
if (!pFileData) {
|
||||
error("File \"%s\" could not be loaded.", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncompress the image
|
||||
if (isPNG)
|
||||
result = ImgLoader::decodePNGImage(pFileData, fileSize, _surface.surfacePtr());
|
||||
else
|
||||
result = ImgLoader::decodeThumbnailImage(pFileData, fileSize, _surface.surfacePtr());
|
||||
|
||||
if (!result) {
|
||||
error("Could not decode image.");
|
||||
delete[] pFileData;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup FileData
|
||||
delete[] pFileData;
|
||||
|
||||
_doCleanup = true;
|
||||
_alphaType = _surface.detectAlpha();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
RenderedImage::RenderedImage(uint width, uint height, bool &result) :
|
||||
_alphaType(Graphics::ALPHA_FULL) {
|
||||
|
||||
_surface.create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
|
||||
|
||||
_backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
|
||||
_doCleanup = true;
|
||||
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
RenderedImage::RenderedImage() : _alphaType(Graphics::ALPHA_FULL) {
|
||||
_backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
|
||||
_surface.format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
||||
|
||||
_doCleanup = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
RenderedImage::~RenderedImage() {
|
||||
if (_doCleanup) {
|
||||
_surface.free();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool RenderedImage::fill(const Common::Rect *pFillRect, uint color) {
|
||||
error("Fill() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool RenderedImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
// Check if PixelData contains enough pixel to create an image with image size equals width * height
|
||||
if (size < static_cast<uint>(_surface.w * _surface.h * 4)) {
|
||||
error("PixelData vector is too small to define a 32 bit %dx%d image.", _surface.w, _surface.h);
|
||||
return false;
|
||||
}
|
||||
|
||||
const byte *in = &pixeldata[offset];
|
||||
byte *out = (byte *)_surface.getPixels();
|
||||
|
||||
for (int i = 0; i < _surface.h; i++) {
|
||||
memcpy(out, in, _surface.w * 4);
|
||||
out += _surface.w * 4;
|
||||
in += stride;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderedImage::replaceContent(byte *pixeldata, int width, int height) {
|
||||
_surface.w = width;
|
||||
_surface.h = height;
|
||||
_surface.pitch = width * 4;
|
||||
_surface.setPixels(pixeldata);
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
uint RenderedImage::getPixel(int x, int y) {
|
||||
error("GetPixel() is not supported. Returning black.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, RectangleList *updateRects) {
|
||||
int newFlipping = (((flipping & 1) ? Graphics::FLIP_V : 0) | ((flipping & 2) ? Graphics::FLIP_H : 0));
|
||||
|
||||
int ca = (color >> BS_ASHIFT) & 0xff;
|
||||
int cr = (color >> BS_RSHIFT) & 0xff;
|
||||
int cg = (color >> BS_GSHIFT) & 0xff;
|
||||
int cb = (color >> BS_BSHIFT) & 0xff;
|
||||
|
||||
Common::Rect srcRect = pPartRect ? *pPartRect : Common::Rect(_surface.w, _surface.h);
|
||||
if (width == -1) width = srcRect.width();
|
||||
if (height == -1) height = srcRect.height();
|
||||
|
||||
_backSurface->blendBlitFrom(_surface, srcRect, Common::Rect(posX, posY, posX + width, posY + height),
|
||||
newFlipping, _surface.format.ARGBToColor(ca, cr, cg, cb), Graphics::BLEND_NORMAL, _alphaType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderedImage::copyDirectly(int posX, int posY) {
|
||||
byte *data = (byte *)_surface.getPixels();
|
||||
int w = _surface.w;
|
||||
int h = _surface.h;
|
||||
|
||||
// Handle off-screen clipping
|
||||
if (posY < 0) {
|
||||
h = MAX(0, (int)_surface.h - -posY);
|
||||
data = (byte *)_surface.getPixels() + _surface.w * -posY;
|
||||
posY = 0;
|
||||
}
|
||||
|
||||
if (posX < 0) {
|
||||
w = MAX(0, (int)_surface.h - -posX);
|
||||
data = (byte *)_surface.getPixels() + (-posX * 4);
|
||||
posX = 0;
|
||||
}
|
||||
|
||||
w = CLIP((int)w, 0, (int)MAX((int)_backSurface->w - posX, 0));
|
||||
h = CLIP((int)h, 0, (int)MAX((int)_backSurface->h - posY, 0));
|
||||
|
||||
g_system->copyRectToScreen(data, _backSurface->pitch, posX, posY, w, h);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
120
engines/sword25/gfx/image/renderedimage.h
Normal file
120
engines/sword25/gfx/image/renderedimage.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RENDERED_IMAGE_H
|
||||
#define SWORD25_RENDERED_IMAGE_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// INCLUDES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class RenderedImage : public Image {
|
||||
private:
|
||||
RenderedImage(const RenderedImage &) : Image(), _doCleanup(false) {}
|
||||
RenderedImage &operator=(const RenderedImage &) { return *this; }
|
||||
public:
|
||||
RenderedImage(const Common::String &filename, bool &result);
|
||||
|
||||
/**
|
||||
@brief Creates an empty BS_RenderedImage
|
||||
|
||||
@param Width The width of the image to be created.
|
||||
@param Height The height of the image to be created
|
||||
@param Result Informs the caller, whether the constructor is executed successfully. If it contains false
|
||||
after the call, do not call methods on the object and destroy the object immediately.
|
||||
*/
|
||||
RenderedImage(uint width, uint height, bool &result);
|
||||
RenderedImage();
|
||||
|
||||
~RenderedImage() override;
|
||||
|
||||
int getWidth() const override {
|
||||
return _surface.w;
|
||||
}
|
||||
int getHeight() const override {
|
||||
return _surface.h;
|
||||
}
|
||||
|
||||
void copyDirectly(int posX, int posY);
|
||||
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) override;
|
||||
bool fill(const Common::Rect *pFillRect, uint color) override;
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0) override;
|
||||
void replaceContent(byte *pixeldata, int width, int height);
|
||||
uint getPixel(int x, int y) override;
|
||||
|
||||
bool isBlitSource() const override {
|
||||
return true;
|
||||
}
|
||||
bool isBlitTarget() const override {
|
||||
return false;
|
||||
}
|
||||
bool isScalingAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isFillingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isAlphaAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isColorModulationAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isSetContentAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void setAlphaType(Graphics::AlphaType alphaType) { _alphaType = alphaType; }
|
||||
bool isSolid() const override { return _alphaType == Graphics::ALPHA_OPAQUE; }
|
||||
|
||||
private:
|
||||
Graphics::ManagedSurface _surface;
|
||||
Graphics::AlphaType _alphaType;
|
||||
bool _doCleanup;
|
||||
|
||||
Graphics::ManagedSurface *_backSurface;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
100
engines/sword25/gfx/image/swimage.cpp
Normal file
100
engines/sword25/gfx/image/swimage.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/gfx/image/imgloader.h"
|
||||
#include "sword25/gfx/image/swimage.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
SWImage::SWImage(const Common::String &filename, bool &result) : _image() {
|
||||
result = false;
|
||||
|
||||
PackageManager *pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
// Load file
|
||||
byte *pFileData;
|
||||
uint fileSize;
|
||||
pFileData = pPackage->getFile(filename, &fileSize);
|
||||
if (!pFileData) {
|
||||
error("File \"%s\" could not be loaded.", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncompress the image
|
||||
if (!ImgLoader::decodePNGImage(pFileData, fileSize, &_image)) {
|
||||
error("Could not decode image.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup FileData
|
||||
delete[] pFileData;
|
||||
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SWImage::~SWImage() {
|
||||
_image.free();
|
||||
}
|
||||
|
||||
|
||||
bool SWImage::blit(int posX, int posY,
|
||||
int flipping,
|
||||
Common::Rect *pPartRect,
|
||||
uint color,
|
||||
int width, int height,
|
||||
RectangleList *updateRects) {
|
||||
error("Blit() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SWImage::fill(const Common::Rect *pFillRect, uint color) {
|
||||
error("Fill() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SWImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
error("SetContent() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint SWImage::getPixel(int x, int y) {
|
||||
assert(x >= 0 && x < _image.w);
|
||||
assert(y >= 0 && y < _image.h);
|
||||
|
||||
byte a, r, g, b;
|
||||
_image.format.colorToARGB(_image.getPixel(x, y), a, r, g, b);
|
||||
|
||||
return BS_ARGB(a, r, g, b);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
90
engines/sword25/gfx/image/swimage.h
Normal file
90
engines/sword25/gfx/image/swimage.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_SWIMAGE_H
|
||||
#define SWORD25_SWIMAGE_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class SWImage : public Image {
|
||||
public:
|
||||
SWImage(const Common::String &filename, bool &result);
|
||||
~SWImage() override;
|
||||
|
||||
int getWidth() const override {
|
||||
return _image.w;
|
||||
}
|
||||
int getHeight() const override {
|
||||
return _image.h;
|
||||
}
|
||||
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) override;
|
||||
bool fill(const Common::Rect *fillRectPtr, uint color) override;
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) override;
|
||||
uint getPixel(int x, int y) override;
|
||||
|
||||
bool isBlitSource() const override {
|
||||
return false;
|
||||
}
|
||||
bool isBlitTarget() const override {
|
||||
return false;
|
||||
}
|
||||
bool isScalingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isFillingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isAlphaAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isColorModulationAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isSetContentAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
Graphics::Surface _image;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
643
engines/sword25/gfx/image/vectorimage.cpp
Normal file
643
engines/sword25/gfx/image/vectorimage.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/gfx/image/art.h"
|
||||
#include "sword25/gfx/image/vectorimage.h"
|
||||
#include "sword25/gfx/image/renderedimage.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
#define BEZSMOOTHNESS 0.5
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SWF datatype
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Bitstream helper class
|
||||
// -----------------------------------------------------------------------------
|
||||
// The parsing of SWF files requires both bitwise readout and on Byte boundaries
|
||||
// oriented reading.
|
||||
// This class is specially equipped for this.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class VectorImage::SWFBitStream {
|
||||
public:
|
||||
SWFBitStream(const byte *pData, uint dataSize) :
|
||||
m_Pos(pData), m_End(pData + dataSize), m_WordMask(0)
|
||||
{}
|
||||
|
||||
inline uint32 getBits(uint bitCount) {
|
||||
if (bitCount == 0 || bitCount > 32) {
|
||||
error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time");
|
||||
}
|
||||
|
||||
uint32 value = 0;
|
||||
while (bitCount) {
|
||||
if (m_WordMask == 0)
|
||||
flushByte();
|
||||
|
||||
value <<= 1;
|
||||
value |= ((m_Word & m_WordMask) != 0) ? 1 : 0;
|
||||
m_WordMask >>= 1;
|
||||
|
||||
--bitCount;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int32 getSignedBits(uint bitCount) {
|
||||
// readout bits
|
||||
uint32 temp = getBits(bitCount);
|
||||
|
||||
// If the sign-bit is set, fill the rest of the return value with 1-bit (sign extension)
|
||||
if (temp & 1 << (bitCount - 1))
|
||||
return (0xffffffff << bitCount) | temp;
|
||||
else
|
||||
return temp;
|
||||
}
|
||||
|
||||
inline uint32 getUInt32() {
|
||||
uint32 byte1 = getByte();
|
||||
uint32 byte2 = getByte();
|
||||
uint32 byte3 = getByte();
|
||||
uint32 byte4 = getByte();
|
||||
|
||||
return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
|
||||
}
|
||||
|
||||
inline uint16 getUInt16() {
|
||||
uint32 byte1 = getByte();
|
||||
uint32 byte2 = getByte();
|
||||
|
||||
return byte1 | (byte2 << 8);
|
||||
}
|
||||
|
||||
inline byte getByte() {
|
||||
flushByte();
|
||||
byte value = m_Word;
|
||||
m_WordMask = 0;
|
||||
flushByte();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline void flushByte() {
|
||||
if (m_WordMask != 128) {
|
||||
if (m_Pos >= m_End) {
|
||||
error("Attempted to read past end of file");
|
||||
} else {
|
||||
m_Word = *m_Pos++;
|
||||
m_WordMask = 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void skipBytes(uint skipLength) {
|
||||
flushByte();
|
||||
if (m_Pos + skipLength >= m_End) {
|
||||
error("Attempted to read past end of file");
|
||||
} else {
|
||||
m_Pos += skipLength;
|
||||
m_Word = *(m_Pos - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const byte *m_Pos;
|
||||
const byte *m_End;
|
||||
|
||||
byte m_Word;
|
||||
uint m_WordMask;
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constants and utility functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constants
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const uint32 MAX_ACCEPTED_FLASH_VERSION = 3; // The maximum flash file version that is accepted by the loader
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Converts SWF rectangle data in a bit stream in Common::Rect objects
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Common::Rect flashRectToBSRect(VectorImage::SWFBitStream &bs) {
|
||||
bs.flushByte();
|
||||
|
||||
// Determines how many bits of the single components are encoded
|
||||
uint32 bitsPerValue = bs.getBits(5);
|
||||
|
||||
// Readout the single components
|
||||
int32 xMin = bs.getSignedBits(bitsPerValue);
|
||||
int32 xMax = bs.getSignedBits(bitsPerValue);
|
||||
int32 yMin = bs.getSignedBits(bitsPerValue);
|
||||
int32 yMax = bs.getSignedBits(bitsPerValue);
|
||||
|
||||
return Common::Rect(xMin, yMin, xMax + 1, yMax + 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Calculate the bounding box of a BS_VectorImageElement
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement) {
|
||||
double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
|
||||
|
||||
for (int j = vectorImageElement.getPathCount() - 1; j >= 0; j--) {
|
||||
ArtBpath *bez = vectorImageElement.getPathInfo(j).getVec();
|
||||
ArtVpath *vec = art_bez_path_to_vec(bez, 0.5);
|
||||
|
||||
if (vec[0].code == ART_END) {
|
||||
free(vec);
|
||||
continue;
|
||||
} else {
|
||||
x0 = x1 = vec[0].x;
|
||||
y0 = y1 = vec[0].y;
|
||||
for (int i = 1; vec[i].code != ART_END; i++) {
|
||||
if (vec[i].x < x0) x0 = vec[i].x;
|
||||
if (vec[i].x > x1) x1 = vec[i].x;
|
||||
if (vec[i].y < y0) y0 = vec[i].y;
|
||||
if (vec[i].y > y1) y1 = vec[i].y;
|
||||
}
|
||||
}
|
||||
free(vec);
|
||||
}
|
||||
|
||||
return Common::Rect(static_cast<int>(x0), static_cast<int>(y0), static_cast<int>(x1) + 1, static_cast<int>(y1) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Construction
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname) : _pixelData(0), _fname(fname) {
|
||||
success = false;
|
||||
_bgColor = 0;
|
||||
|
||||
// Create bitstream object
|
||||
// In the following the file data will be readout of the bitstream object.
|
||||
SWFBitStream bs(pFileData, fileSize);
|
||||
|
||||
// Check SWF signature
|
||||
uint32 signature[3];
|
||||
signature[0] = bs.getByte();
|
||||
signature[1] = bs.getByte();
|
||||
signature[2] = bs.getByte();
|
||||
if (signature[0] != 'F' ||
|
||||
signature[1] != 'W' ||
|
||||
signature[2] != 'S') {
|
||||
error("File is not a valid SWF-file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the version
|
||||
uint32 version = bs.getByte();
|
||||
if (version > MAX_ACCEPTED_FLASH_VERSION) {
|
||||
error("File is of version %d. Highest accepted version is %d.", version, MAX_ACCEPTED_FLASH_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Readout filesize and compare with the actual size
|
||||
uint32 storedFileSize = bs.getUInt32();
|
||||
if (storedFileSize != fileSize) {
|
||||
error("File is not a valid SWF-file");
|
||||
return;
|
||||
}
|
||||
|
||||
// readout SWF size
|
||||
flashRectToBSRect(bs);
|
||||
|
||||
// Get frame rate and frame count
|
||||
/* uint32 frameRate = */
|
||||
bs.getUInt16();
|
||||
/* uint32 frameCount = */
|
||||
bs.getUInt16();
|
||||
|
||||
// Parse tags
|
||||
// Because we are only interested in the first DifneShape-Tag...
|
||||
bool keepParsing = true;
|
||||
while (keepParsing) {
|
||||
// Tags always begin on byte boundaries
|
||||
bs.flushByte();
|
||||
|
||||
// Readout tag type and length
|
||||
uint16 tagTypeAndLength = bs.getUInt16();
|
||||
uint32 tagType = tagTypeAndLength >> 6;
|
||||
uint32 tagLength = tagTypeAndLength & 0x3f;
|
||||
if (tagLength == 0x3f)
|
||||
tagLength = bs.getUInt32();
|
||||
|
||||
switch (tagType) {
|
||||
case 2:
|
||||
// DefineShape
|
||||
success = parseDefineShape(2, bs);
|
||||
return;
|
||||
case 22:
|
||||
// DefineShape2
|
||||
success = parseDefineShape(2, bs);
|
||||
return;
|
||||
case 32:
|
||||
success = parseDefineShape(3, bs);
|
||||
return;
|
||||
case 9:
|
||||
// SetBackgroundColor
|
||||
{
|
||||
byte r, g, b;
|
||||
r = bs.getByte();
|
||||
g = bs.getByte();
|
||||
b = bs.getByte();
|
||||
_bgColor = BS_RGB(r, g, b);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("Ignoring tag: %d, %d bytes", tagType, tagLength);
|
||||
// Ignore unknown tags
|
||||
bs.skipBytes(tagLength);
|
||||
}
|
||||
}
|
||||
|
||||
// The execution must not arrive at this point: Either a shape is found, then the function will be leaved before, or it is found none, then
|
||||
// an exception occurs as soon as it is read beyond of the end of file.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
VectorImage::~VectorImage() {
|
||||
for (int j = _elements.size() - 1; j >= 0; j--)
|
||||
for (int i = _elements[j].getPathCount() - 1; i >= 0; i--)
|
||||
if (_elements[j].getPathInfo(i).getVec())
|
||||
free(_elements[j].getPathInfo(i).getVec());
|
||||
|
||||
free(_pixelData);
|
||||
}
|
||||
|
||||
|
||||
ArtBpath *ensureBezStorage(ArtBpath *bez, int nodes, int *allocated) {
|
||||
if (*allocated <= nodes) {
|
||||
(*allocated) += 20;
|
||||
|
||||
return art_renew(bez, ArtBpath, *allocated);
|
||||
}
|
||||
|
||||
return bez;
|
||||
}
|
||||
|
||||
ArtBpath *VectorImage::storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated) {
|
||||
(*bezNodes)++;
|
||||
|
||||
bez = ensureBezStorage(bez, *bezNodes, bezAllocated);
|
||||
bez[*bezNodes].code = ART_END;
|
||||
|
||||
ArtBpath *bez1 = art_new(ArtBpath, *bezNodes + 1);
|
||||
if (!bez1)
|
||||
error("[VectorImage::storeBez] Cannot allocate memory");
|
||||
|
||||
for (int i = 0; i <= *bezNodes; i++)
|
||||
bez1[i] = bez[i];
|
||||
|
||||
_elements.back()._pathInfos.push_back(VectorPathInfo(bez1, *bezNodes, lineStyle, fillStyle0, fillStyle1));
|
||||
|
||||
return bez;
|
||||
}
|
||||
|
||||
bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {
|
||||
/*uint32 shapeID = */bs.getUInt16();
|
||||
|
||||
// readout bounding box
|
||||
_boundingBox = flashRectToBSRect(bs);
|
||||
|
||||
// create first image element
|
||||
_elements.resize(1);
|
||||
|
||||
// read styles
|
||||
uint numFillBits;
|
||||
uint numLineBits;
|
||||
if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
|
||||
return false;
|
||||
|
||||
uint lineStyle = 0;
|
||||
uint fillStyle0 = 0;
|
||||
uint fillStyle1 = 0;
|
||||
|
||||
// parse shaperecord
|
||||
// ------------------
|
||||
|
||||
double curX = 0;
|
||||
double curY = 0;
|
||||
int bezNodes = 0;
|
||||
int bezAllocated = 10;
|
||||
ArtBpath *bez = art_new(ArtBpath, bezAllocated);
|
||||
|
||||
bool endOfShapeDiscovered = false;
|
||||
while (!endOfShapeDiscovered) {
|
||||
uint32 typeFlag = bs.getBits(1);
|
||||
|
||||
// Non-Edge Record
|
||||
if (typeFlag == 0) {
|
||||
// Determines which parameters are set
|
||||
uint32 stateNewStyles = bs.getBits(1);
|
||||
uint32 stateLineStyle = bs.getBits(1);
|
||||
uint32 stateFillStyle1 = bs.getBits(1);
|
||||
uint32 stateFillStyle0 = bs.getBits(1);
|
||||
uint32 stateMoveTo = bs.getBits(1);
|
||||
|
||||
uint prevLineStyle = lineStyle;
|
||||
uint prevFillStyle0 = fillStyle0;
|
||||
uint prevFillStyle1 = fillStyle1;
|
||||
|
||||
// End of the shape definition is reached?
|
||||
if (!stateNewStyles && !stateLineStyle && !stateFillStyle0 && !stateFillStyle1 && !stateMoveTo) {
|
||||
endOfShapeDiscovered = true;
|
||||
// Decode parameters
|
||||
} else {
|
||||
if (stateMoveTo) {
|
||||
uint32 moveToBits = bs.getBits(5);
|
||||
curX = bs.getSignedBits(moveToBits);
|
||||
curY = bs.getSignedBits(moveToBits);
|
||||
}
|
||||
|
||||
if (stateFillStyle0) {
|
||||
if (numFillBits > 0)
|
||||
fillStyle0 = bs.getBits(numFillBits);
|
||||
else
|
||||
fillStyle0 = 0;
|
||||
}
|
||||
|
||||
if (stateFillStyle1) {
|
||||
if (numFillBits > 0)
|
||||
fillStyle1 = bs.getBits(numFillBits);
|
||||
else
|
||||
fillStyle1 = 0;
|
||||
}
|
||||
|
||||
if (stateLineStyle) {
|
||||
if (numLineBits)
|
||||
lineStyle = bs.getBits(numLineBits);
|
||||
else
|
||||
numLineBits = 0;
|
||||
}
|
||||
|
||||
// Create a new path, unless there were only defined new styles
|
||||
if (stateLineStyle || stateFillStyle0 || stateFillStyle1 || stateMoveTo) {
|
||||
// Store previous curve if any
|
||||
if (bezNodes) {
|
||||
bez = storeBez(bez, prevLineStyle, prevFillStyle0, prevFillStyle1, &bezNodes, &bezAllocated);
|
||||
}
|
||||
|
||||
// Start new curve
|
||||
bez = ensureBezStorage(bez, 1, &bezAllocated);
|
||||
bez[0].code = ART_MOVETO_OPEN;
|
||||
bez[0].x3 = curX;
|
||||
bez[0].y3 = curY;
|
||||
bezNodes = 0;
|
||||
}
|
||||
|
||||
if (stateNewStyles) {
|
||||
// The old style definitions will be discarded and overwritten with the new at this point in Flash.
|
||||
// A new element will be started with a new element.
|
||||
_elements.resize(_elements.size() + 1);
|
||||
if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Edge record
|
||||
uint32 edgeFlag = bs.getBits(1);
|
||||
uint32 numBits = bs.getBits(4) + 2;
|
||||
|
||||
// Curved edge
|
||||
if (edgeFlag == 0) {
|
||||
double controlDeltaX = bs.getSignedBits(numBits);
|
||||
double controlDeltaY = bs.getSignedBits(numBits);
|
||||
double anchorDeltaX = bs.getSignedBits(numBits);
|
||||
double anchorDeltaY = bs.getSignedBits(numBits);
|
||||
|
||||
double controlX = curX + controlDeltaX;
|
||||
double controlY = curY + controlDeltaY;
|
||||
double newX = controlX + anchorDeltaX;
|
||||
double newY = controlY + anchorDeltaY;
|
||||
|
||||
#define WEIGHT (2.0/3.0)
|
||||
|
||||
bezNodes++;
|
||||
bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
|
||||
bez[bezNodes].code = ART_CURVETO;
|
||||
bez[bezNodes].x1 = WEIGHT * controlX + (1 - WEIGHT) * curX;
|
||||
bez[bezNodes].y1 = WEIGHT * controlY + (1 - WEIGHT) * curY;
|
||||
bez[bezNodes].x2 = WEIGHT * controlX + (1 - WEIGHT) * newX;
|
||||
bez[bezNodes].y2 = WEIGHT * controlY + (1 - WEIGHT) * newY;
|
||||
bez[bezNodes].x3 = newX;
|
||||
bez[bezNodes].y3 = newY;
|
||||
|
||||
curX = newX;
|
||||
curY = newY;
|
||||
} else {
|
||||
// Staight edge
|
||||
int32 deltaX = 0;
|
||||
int32 deltaY = 0;
|
||||
|
||||
uint32 generalLineFlag = bs.getBits(1);
|
||||
if (generalLineFlag) {
|
||||
deltaX = bs.getSignedBits(numBits);
|
||||
deltaY = bs.getSignedBits(numBits);
|
||||
} else {
|
||||
uint32 vertLineFlag = bs.getBits(1);
|
||||
if (vertLineFlag)
|
||||
deltaY = bs.getSignedBits(numBits);
|
||||
else
|
||||
deltaX = bs.getSignedBits(numBits);
|
||||
}
|
||||
|
||||
curX += deltaX;
|
||||
curY += deltaY;
|
||||
|
||||
bezNodes++;
|
||||
bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
|
||||
bez[bezNodes].code = ART_LINETO;
|
||||
bez[bezNodes].x3 = curX;
|
||||
bez[bezNodes].y3 = curY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store last curve
|
||||
if (bezNodes)
|
||||
bez = storeBez(bez, lineStyle, fillStyle0, fillStyle1, &bezNodes, &bezAllocated);
|
||||
|
||||
free(bez);
|
||||
|
||||
// Calculate the bounding boxes of each element
|
||||
Common::Array<VectorImageElement>::iterator it = _elements.begin();
|
||||
for (; it != _elements.end(); ++it)
|
||||
it->_boundingBox = CalculateBoundingBox(*it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool VectorImage::parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits) {
|
||||
bs.flushByte();
|
||||
|
||||
// Parse fill styles
|
||||
// -----------------
|
||||
|
||||
// Determine number of fill styles
|
||||
uint fillStyleCount = bs.getByte();
|
||||
if (fillStyleCount == 0xff)
|
||||
fillStyleCount = bs.getUInt16();
|
||||
|
||||
// Readout all fill styles. If a fill style with Typ != 0 is found, the parsing is aborted.
|
||||
// Only "solid fill" (Typ 0) is supported.
|
||||
_elements.back()._fillStyles.reserve(fillStyleCount);
|
||||
for (uint i = 0; i < fillStyleCount; ++i) {
|
||||
byte type = bs.getByte();
|
||||
uint32 color;
|
||||
byte r = bs.getByte();
|
||||
byte g = bs.getByte();
|
||||
byte b = bs.getByte();
|
||||
byte a = 0xff;
|
||||
|
||||
if (shapeType == 3)
|
||||
a = bs.getByte();
|
||||
|
||||
color = BS_ARGB(a, r, g, b);
|
||||
|
||||
if (type != 0)
|
||||
return false;
|
||||
|
||||
_elements.back()._fillStyles.push_back(color);
|
||||
}
|
||||
|
||||
// Line styles parsen
|
||||
// -----------------
|
||||
|
||||
// Determine number of line styles
|
||||
uint lineStyleCount = bs.getByte();
|
||||
if (lineStyleCount == 0xff)
|
||||
lineStyleCount = bs.getUInt16();
|
||||
|
||||
// Readout all line styles
|
||||
_elements.back()._lineStyles.reserve(lineStyleCount);
|
||||
for (uint i = 0; i < lineStyleCount; ++i) {
|
||||
double width = bs.getUInt16();
|
||||
uint32 color;
|
||||
byte r = bs.getByte();
|
||||
byte g = bs.getByte();
|
||||
byte b = bs.getByte();
|
||||
byte a = 0xff;
|
||||
|
||||
if (shapeType == 3)
|
||||
a = bs.getByte();
|
||||
|
||||
color = BS_ARGB(a, r, g, b);
|
||||
|
||||
_elements.back()._lineStyles.push_back(VectorImageElement::LineStyleType(width, color));
|
||||
}
|
||||
|
||||
// Readout the bit width for the following style indices
|
||||
numFillBits = bs.getBits(4);
|
||||
numLineBits = bs.getBits(4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool VectorImage::fill(const Common::Rect *pFillRect, uint color) {
|
||||
error("Fill() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
uint VectorImage::getPixel(int x, int y) {
|
||||
error("GetPixel() is not supported. Returning black.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool VectorImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
error("SetContent() is not supported.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VectorImage::blit(int posX, int posY,
|
||||
int flipping,
|
||||
Common::Rect *pPartRect,
|
||||
uint color,
|
||||
int width, int height,
|
||||
RectangleList *updateRects) {
|
||||
static VectorImage *oldThis = 0;
|
||||
static int oldWidth = -2;
|
||||
static int oldHeight = -2;
|
||||
|
||||
// If width or height to 0, nothing needs to be shown.
|
||||
if (width == 0 || height == 0)
|
||||
return true;
|
||||
|
||||
// Determine if the old image in the cache can not be reused and must be recalculated
|
||||
if (!(oldThis == this && oldWidth == width && oldHeight == height)) {
|
||||
render(width, height);
|
||||
|
||||
oldThis = this;
|
||||
oldHeight = height;
|
||||
oldWidth = width;
|
||||
}
|
||||
|
||||
RenderedImage *rend = new RenderedImage();
|
||||
|
||||
rend->replaceContent(_pixelData, width, height);
|
||||
rend->blit(posX, posY, flipping, pPartRect, color, width, height, updateRects);
|
||||
|
||||
delete rend;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
232
engines/sword25/gfx/image/vectorimage.h
Normal file
232
engines/sword25/gfx/image/vectorimage.h
Normal file
@@ -0,0 +1,232 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_VECTORIMAGE_H
|
||||
#define SWORD25_VECTORIMAGE_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "art.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class VectorImage;
|
||||
|
||||
/**
|
||||
@brief Pfadinformationen zu BS_VectorImageElement Objekten
|
||||
|
||||
Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs
|
||||
gespeichert werden.
|
||||
*/
|
||||
|
||||
class VectorPathInfo {
|
||||
public:
|
||||
VectorPathInfo(ArtBpath *vec, int len, uint lineStyle, uint fillStyle0, uint fillStyle1) :
|
||||
_vec(vec), _lineStyle(lineStyle), _fillStyle0(fillStyle0), _fillStyle1(fillStyle1), _len(len) {}
|
||||
|
||||
VectorPathInfo() {
|
||||
_lineStyle = _fillStyle0 = _fillStyle1 = _len = 0;
|
||||
_vec = 0;
|
||||
}
|
||||
|
||||
ArtBpath *getVec() const {
|
||||
return _vec;
|
||||
}
|
||||
int getVecLen() const {
|
||||
return _len;
|
||||
}
|
||||
uint getLineStyle() const {
|
||||
return _lineStyle;
|
||||
}
|
||||
uint getFillStyle0() const {
|
||||
return _fillStyle0;
|
||||
}
|
||||
uint getFillStyle1() const {
|
||||
return _fillStyle1;
|
||||
}
|
||||
|
||||
private:
|
||||
ArtBpath *_vec;
|
||||
uint _lineStyle;
|
||||
uint _fillStyle0;
|
||||
uint _fillStyle1;
|
||||
uint _len;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren.
|
||||
Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild.
|
||||
*/
|
||||
class VectorImageElement {
|
||||
friend class VectorImage;
|
||||
public:
|
||||
uint getPathCount() const {
|
||||
return _pathInfos.size();
|
||||
}
|
||||
const VectorPathInfo &getPathInfo(uint pathNr) const {
|
||||
assert(pathNr < getPathCount());
|
||||
return _pathInfos[pathNr];
|
||||
}
|
||||
|
||||
double getLineStyleWidth(uint lineStyle) const {
|
||||
assert(lineStyle < _lineStyles.size());
|
||||
return _lineStyles[lineStyle].width;
|
||||
}
|
||||
|
||||
uint getLineStyleCount() const {
|
||||
return _lineStyles.size();
|
||||
}
|
||||
|
||||
uint32 getLineStyleColor(uint lineStyle) const {
|
||||
assert(lineStyle < _lineStyles.size());
|
||||
return _lineStyles[lineStyle].color;
|
||||
}
|
||||
|
||||
uint getFillStyleCount() const {
|
||||
return _fillStyles.size();
|
||||
}
|
||||
|
||||
uint32 getFillStyleColor(uint fillStyle) const {
|
||||
assert(fillStyle < _fillStyles.size());
|
||||
return _fillStyles[fillStyle];
|
||||
}
|
||||
|
||||
const Common::Rect &getBoundingBox() const {
|
||||
return _boundingBox;
|
||||
}
|
||||
|
||||
private:
|
||||
struct LineStyleType {
|
||||
LineStyleType(double width_, uint32 color_) : width(width_), color(color_) {}
|
||||
LineStyleType() {
|
||||
width = 0;
|
||||
color = 0;
|
||||
}
|
||||
double width;
|
||||
uint32 color;
|
||||
};
|
||||
|
||||
Common::Array<VectorPathInfo> _pathInfos;
|
||||
Common::Array<LineStyleType> _lineStyles;
|
||||
Common::Array<uint32> _fillStyles;
|
||||
Common::Rect _boundingBox;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Eine Vektorgraphik
|
||||
|
||||
Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes.
|
||||
*/
|
||||
|
||||
class VectorImage : public Image {
|
||||
public:
|
||||
VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname);
|
||||
~VectorImage() override;
|
||||
|
||||
uint getElementCount() const {
|
||||
return _elements.size();
|
||||
}
|
||||
const VectorImageElement &getElement(uint elementNr) const {
|
||||
assert(elementNr < _elements.size());
|
||||
return _elements[elementNr];
|
||||
}
|
||||
const Common::Rect &getBoundingBox() const {
|
||||
return _boundingBox;
|
||||
}
|
||||
|
||||
//
|
||||
// Die abstrakten Methoden von BS_Image
|
||||
//
|
||||
int getWidth() const override {
|
||||
return _boundingBox.width();
|
||||
}
|
||||
int getHeight() const override {
|
||||
return _boundingBox.height();
|
||||
}
|
||||
bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) override;
|
||||
|
||||
void render(int width, int height);
|
||||
|
||||
uint getPixel(int x, int y) override;
|
||||
bool isBlitSource() const override {
|
||||
return true;
|
||||
}
|
||||
bool isBlitTarget() const override {
|
||||
return false;
|
||||
}
|
||||
bool isScalingAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isFillingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isAlphaAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isColorModulationAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isSetContentAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) override;
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) override;
|
||||
|
||||
class SWFBitStream;
|
||||
|
||||
private:
|
||||
bool parseDefineShape(uint shapeType, SWFBitStream &bs);
|
||||
bool parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits);
|
||||
|
||||
ArtBpath *storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated);
|
||||
Common::Array<VectorImageElement> _elements;
|
||||
Common::Rect _boundingBox;
|
||||
|
||||
byte *_pixelData;
|
||||
|
||||
Common::String _fname;
|
||||
uint _bgColor;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
470
engines/sword25/gfx/image/vectorimagerenderer.cpp
Normal file
470
engines/sword25/gfx/image/vectorimagerenderer.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code contains portions of Libart_LGPL - library of basic graphic primitives
|
||||
*
|
||||
* Copyright (c) 1998 Raph Levien
|
||||
*
|
||||
* Licensed under GNU LGPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code contains portions of Swfdec
|
||||
*
|
||||
* Copyright (c) 2004-2006 David Schleef <ds@schleef.org>
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/image/art.h"
|
||||
#include "sword25/gfx/image/vectorimage.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
void art_rgb_fill_run1(byte *buf, byte r, byte g, byte b, int n) {
|
||||
int i;
|
||||
|
||||
if (r == g && g == b && r == 255) {
|
||||
memset(buf, g, n + n + n + n);
|
||||
} else {
|
||||
uint32 *alt = (uint32 *)buf;
|
||||
uint32 color = MS_RGB(r, g, b);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
*alt++ = color;
|
||||
}
|
||||
}
|
||||
|
||||
void art_rgb_run_alpha1(byte *buf, byte r, byte g, byte b, int alpha, int n) {
|
||||
int i;
|
||||
int v;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
#if defined(SCUMM_LITTLE_ENDIAN)
|
||||
v = *buf;
|
||||
*buf++ = MIN(v + alpha, 0xff);
|
||||
v = *buf;
|
||||
*buf++ = v + (((b - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((g - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((r - v) * alpha + 0x80) >> 8);
|
||||
#else
|
||||
v = *buf;
|
||||
*buf++ = v + (((r - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((g - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((b - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = MIN(v + alpha, 0xff);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
|
||||
|
||||
struct _ArtRgbSVPAlphaData {
|
||||
int alphatab[256];
|
||||
byte r, g, b, alpha;
|
||||
byte *buf;
|
||||
int rowstride;
|
||||
int x0, x1;
|
||||
};
|
||||
|
||||
static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
|
||||
int start, ArtSVPRenderAAStep *steps, int n_steps) {
|
||||
ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
|
||||
byte *linebuf;
|
||||
int run_x0, run_x1;
|
||||
uint32 running_sum = start;
|
||||
int x0, x1;
|
||||
int k;
|
||||
byte r, g, b;
|
||||
int *alphatab;
|
||||
int alpha;
|
||||
|
||||
linebuf = data->buf;
|
||||
x0 = data->x0;
|
||||
x1 = data->x1;
|
||||
|
||||
r = data->r;
|
||||
g = data->g;
|
||||
b = data->b;
|
||||
alphatab = data->alphatab;
|
||||
|
||||
if (n_steps > 0) {
|
||||
run_x1 = steps[0].x;
|
||||
if (run_x1 > x0) {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
|
||||
}
|
||||
|
||||
for (k = 0; k < n_steps - 1; k++) {
|
||||
running_sum += steps[k].delta;
|
||||
run_x0 = run_x1;
|
||||
run_x1 = steps[k + 1].x;
|
||||
if (run_x1 > run_x0) {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
|
||||
}
|
||||
}
|
||||
running_sum += steps[k].delta;
|
||||
if (x1 > run_x1) {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
|
||||
}
|
||||
} else {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
|
||||
}
|
||||
|
||||
data->buf += data->rowstride;
|
||||
}
|
||||
|
||||
static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
|
||||
int start,
|
||||
ArtSVPRenderAAStep *steps, int n_steps) {
|
||||
ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
|
||||
byte *linebuf;
|
||||
int run_x0, run_x1;
|
||||
uint32 running_sum = start;
|
||||
int x0, x1;
|
||||
int k;
|
||||
byte r, g, b;
|
||||
int *alphatab;
|
||||
int alpha;
|
||||
|
||||
linebuf = data->buf;
|
||||
x0 = data->x0;
|
||||
x1 = data->x1;
|
||||
|
||||
r = data->r;
|
||||
g = data->g;
|
||||
b = data->b;
|
||||
alphatab = data->alphatab;
|
||||
|
||||
if (n_steps > 0) {
|
||||
run_x1 = steps[0].x;
|
||||
if (run_x1 > x0) {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < n_steps - 1; k++) {
|
||||
running_sum += steps[k].delta;
|
||||
run_x0 = run_x1;
|
||||
run_x1 = steps[k + 1].x;
|
||||
if (run_x1 > run_x0) {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
running_sum += steps[k].delta;
|
||||
if (x1 > run_x1) {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
|
||||
}
|
||||
}
|
||||
|
||||
data->buf += data->rowstride;
|
||||
}
|
||||
|
||||
void art_rgb_svp_alpha1(const ArtSVP *svp,
|
||||
int x0, int y0, int x1, int y1,
|
||||
uint32 color,
|
||||
byte *buf, int rowstride) {
|
||||
ArtRgbSVPAlphaData data;
|
||||
int i;
|
||||
int a, da;
|
||||
|
||||
data.r = (color >> 16) & 0xFF;
|
||||
data.g = (color >> 8) & 0xFF;
|
||||
data.b = (color >> 0) & 0xFF;
|
||||
data.alpha = (color >> 24) & 0xFF;
|
||||
|
||||
a = 0x8000;
|
||||
da = (data.alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
data.alphatab[i] = a >> 16;
|
||||
a += da;
|
||||
}
|
||||
|
||||
data.buf = buf;
|
||||
data.rowstride = rowstride;
|
||||
data.x0 = x0;
|
||||
data.x1 = x1;
|
||||
if (data.alpha == 255)
|
||||
art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
|
||||
else
|
||||
art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
|
||||
}
|
||||
|
||||
static int art_vpath_len(ArtVpath *a) {
|
||||
int i = 0;
|
||||
while (a[i].code != ART_END)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
|
||||
ArtVpath *dest;
|
||||
ArtVpath *p;
|
||||
int len_a, len_b;
|
||||
|
||||
len_a = art_vpath_len(a);
|
||||
len_b = art_vpath_len(b);
|
||||
dest = art_new(ArtVpath, len_a + len_b + 1);
|
||||
if (!dest)
|
||||
error("[art_vpath_cat] Cannot allocate memory");
|
||||
|
||||
p = dest;
|
||||
|
||||
for (int i = 0; i < len_a; i++)
|
||||
*p++ = *a++;
|
||||
for (int i = 0; i <= len_b; i++)
|
||||
*p++ = *b++;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void art_svp_make_convex(ArtSVP *svp) {
|
||||
int i;
|
||||
|
||||
if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
|
||||
for (i = 0; i < svp->n_segs; i++) {
|
||||
svp->segs[i].dir = !svp->segs[i].dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArtVpath *art_vpath_reverse(ArtVpath *a) {
|
||||
ArtVpath *dest;
|
||||
ArtVpath it;
|
||||
int len;
|
||||
int state = 0;
|
||||
int i;
|
||||
|
||||
len = art_vpath_len(a);
|
||||
dest = art_new(ArtVpath, len + 1);
|
||||
if (!dest)
|
||||
error("[art_vpath_reverse] Cannot allocate memory");
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
it = a[len - i - 1];
|
||||
if (state) {
|
||||
it.code = ART_LINETO;
|
||||
} else {
|
||||
it.code = ART_MOVETO_OPEN;
|
||||
state = 1;
|
||||
}
|
||||
if (a[len - i - 1].code == ART_MOVETO ||
|
||||
a[len - i - 1].code == ART_MOVETO_OPEN) {
|
||||
state = 0;
|
||||
}
|
||||
dest[i] = it;
|
||||
}
|
||||
dest[len] = a[len];
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
|
||||
ArtVpath *dest;
|
||||
|
||||
dest = art_vpath_reverse(a);
|
||||
free(a);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void drawBez(ArtBpath *bez1, ArtBpath *bez2, byte *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
|
||||
ArtVpath *vec = NULL;
|
||||
ArtVpath *vec1 = NULL;
|
||||
ArtVpath *vec2 = NULL;
|
||||
ArtSVP *svp = NULL;
|
||||
|
||||
#if 0
|
||||
const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
|
||||
for (int i = 0;; i++) {
|
||||
debugN(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
|
||||
if (bez[i].code == ART_END)
|
||||
break;
|
||||
if (bez[i].code == ART_CURVETO) {
|
||||
debugN(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
|
||||
debugN(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
|
||||
}
|
||||
debugN(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
|
||||
}
|
||||
|
||||
debugN(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
|
||||
#endif
|
||||
|
||||
// HACK: Some frames have green bounding boxes drawn.
|
||||
// Perhaps they were used by original game artist Umriss
|
||||
// We skip them just like the original
|
||||
if (bez2 == 0 && color == BS_RGB(0x00, 0xff, 0x00)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec1 = art_bez_path_to_vec(bez1, 0.5);
|
||||
if (bez2 != 0) {
|
||||
vec2 = art_bez_path_to_vec(bez2, 0.5);
|
||||
vec2 = art_vpath_reverse_free(vec2);
|
||||
vec = art_vpath_cat(vec1, vec2);
|
||||
|
||||
free(vec1);
|
||||
free(vec2);
|
||||
} else {
|
||||
vec = vec1;
|
||||
}
|
||||
|
||||
int size = art_vpath_len(vec);
|
||||
ArtVpath *vect = art_new(ArtVpath, size + 1);
|
||||
if (!vect)
|
||||
error("[drawBez] Cannot allocate memory");
|
||||
|
||||
int k;
|
||||
for (k = 0; k < size; k++) {
|
||||
vect[k].code = vec[k].code;
|
||||
vect[k].x = (vec[k].x - deltaX) * scaleX;
|
||||
vect[k].y = (vec[k].y - deltaY) * scaleY;
|
||||
}
|
||||
vect[k].code = ART_END;
|
||||
|
||||
if (bez2 == 0) { // Line drawing
|
||||
svp = art_svp_vpath_stroke(vect, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
|
||||
} else {
|
||||
svp = art_svp_from_vpath(vect);
|
||||
art_svp_make_convex(svp);
|
||||
}
|
||||
|
||||
art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
|
||||
|
||||
free(vect);
|
||||
art_svp_free(svp);
|
||||
free(vec);
|
||||
}
|
||||
|
||||
void VectorImage::render(int width, int height) {
|
||||
double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
|
||||
double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
|
||||
|
||||
debug(3, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
|
||||
|
||||
if (_pixelData)
|
||||
free(_pixelData);
|
||||
|
||||
_pixelData = (byte *)malloc(width * height * 4);
|
||||
memset(_pixelData, 0, width * height * 4);
|
||||
|
||||
for (uint e = 0; e < _elements.size(); e++) {
|
||||
|
||||
//// Draw shapes
|
||||
for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
|
||||
int fill0len = 0;
|
||||
int fill1len = 0;
|
||||
|
||||
// Count vector sizes in order to minimize memory
|
||||
// fragmentation
|
||||
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
||||
if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
|
||||
fill0len += _elements[e].getPathInfo(p).getVecLen();
|
||||
|
||||
if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
|
||||
fill1len += _elements[e].getPathInfo(p).getVecLen();
|
||||
}
|
||||
|
||||
// Now lump together vectors
|
||||
ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
|
||||
ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
|
||||
ArtBpath *fill1pos = fill1;
|
||||
ArtBpath *fill0pos = fill0;
|
||||
|
||||
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
||||
if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
|
||||
for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
|
||||
*fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
|
||||
}
|
||||
|
||||
if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
|
||||
for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
|
||||
*fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Close vectors
|
||||
(*fill0pos).code = ART_END;
|
||||
(*fill1pos).code = ART_END;
|
||||
|
||||
drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
|
||||
|
||||
free(fill0);
|
||||
free(fill1);
|
||||
}
|
||||
|
||||
//// Draw strokes
|
||||
for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
|
||||
double penWidth = _elements[e].getLineStyleWidth(s);
|
||||
penWidth *= sqrt(fabs(scaleX * scaleY));
|
||||
|
||||
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
||||
if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
|
||||
drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Sword25
|
||||
Reference in New Issue
Block a user