Merge tag '2.5.2'
Conflicts: lime/graphics/utils/ImageDataUtil.hx
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
<set name="NATIVE_TOOLKIT_PATH" value="lib" unless="NATIVE_TOOLKIT_PATH" />
|
||||
|
||||
<set name="NATIVE_TOOLKIT_CURL_SSL" value="1" />
|
||||
<set name="NATIVE_TOOLKIT_CURL_DISABLE_AXTLS_NONBLOCKING" value="1" />
|
||||
<set name="NATIVE_TOOLKIT_SDL_STATIC" value="1" />
|
||||
|
||||
<set name="NATIVE_TOOLKIT_SDL_ANGLE" value="1" if="LIME_SDL_ANGLE" />
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace lime {
|
||||
int height;
|
||||
int offsetX;
|
||||
int offsetY;
|
||||
bool transparent;
|
||||
int width;
|
||||
|
||||
private:
|
||||
|
||||
@@ -28,8 +28,9 @@ namespace lime {
|
||||
Bytes *data;
|
||||
PixelFormat format;
|
||||
int height;
|
||||
int width;
|
||||
bool premultiplied;
|
||||
bool transparent;
|
||||
int width;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ namespace lime {
|
||||
|
||||
enum PixelFormat {
|
||||
|
||||
RGBA,
|
||||
ARGB,
|
||||
BGRA
|
||||
RGBA32,
|
||||
ARGB32,
|
||||
BGRA32
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace lime {
|
||||
|
||||
virtual void Flip () = 0;
|
||||
virtual value Lock () = 0;
|
||||
virtual const char* Type () = 0;
|
||||
virtual void Unlock () = 0;
|
||||
|
||||
Window* currentWindow;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <math/Vector2.h>
|
||||
#include <system/System.h>
|
||||
#include <utils/Bytes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace lime {
|
||||
@@ -22,9 +23,9 @@ namespace lime {
|
||||
|
||||
static void ColorTransform (Image* image, Rectangle* rect, ColorMatrix* ColorMatrix);
|
||||
static void CopyChannel (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int srcChannel, int destChannel);
|
||||
static void CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, bool mergeAlpha);
|
||||
static void FillRect (Image* image, Rectangle* rect, int color);
|
||||
static void FloodFill (Image* image, int x, int y, int color);
|
||||
static void CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, Image* alphaImage, Vector2* alphaPoint, bool mergeAlpha);
|
||||
static void FillRect (Image* image, Rectangle* rect, int32_t color);
|
||||
static void FloodFill (Image* image, int x, int y, int32_t color);
|
||||
static void GetPixels (Image* image, Rectangle* rect, PixelFormat format, Bytes* pixels);
|
||||
static void Merge (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int redMultiplier, int greenMultiplier, int blueMultiplier, int alphaMultiplier);
|
||||
static void MultiplyAlpha (Image* image);
|
||||
@@ -37,6 +38,32 @@ namespace lime {
|
||||
};
|
||||
|
||||
|
||||
class ImageDataView {
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ImageDataView (Image* image, Rectangle* rect);
|
||||
|
||||
void Clip (int x, int y, int width, int height);
|
||||
int Row (int y);
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
private:
|
||||
|
||||
Image* image;
|
||||
int offset;
|
||||
Rectangle* rect;
|
||||
int stride;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <hx/CFFI.h>
|
||||
#include <system/System.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace lime {
|
||||
@@ -20,16 +21,24 @@ namespace lime {
|
||||
|
||||
float GetAlphaMultiplier ();
|
||||
float GetAlphaOffset ();
|
||||
void GetAlphaTable (unsigned char* table);
|
||||
float GetBlueMultiplier ();
|
||||
float GetBlueOffset ();
|
||||
int GetColor ();
|
||||
void GetBlueTable (unsigned char* table);
|
||||
int32_t GetColor ();
|
||||
float GetGreenMultiplier ();
|
||||
float GetGreenOffset ();
|
||||
void GetGreenTable (unsigned char* table);
|
||||
float GetRedMultiplier ();
|
||||
float GetRedOffset ();
|
||||
void GetRedTable (unsigned char* table);
|
||||
|
||||
float data[20];
|
||||
|
||||
private:
|
||||
|
||||
void GetDataTable (unsigned char* table, float multiplier, float offset);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace lime {
|
||||
Rectangle (double x, double y, double width, double height);
|
||||
Rectangle (value rect);
|
||||
|
||||
void Contract (double x, double y, double width, double height);
|
||||
|
||||
double height;
|
||||
double width;
|
||||
double x;
|
||||
|
||||
211
project/include/math/color/RGBA.h
Normal file
211
project/include/math/color/RGBA.h
Normal file
@@ -0,0 +1,211 @@
|
||||
#ifndef LIME_MATH_COLOR_RGBA_H
|
||||
#define LIME_MATH_COLOR_RGBA_H
|
||||
|
||||
|
||||
#include <graphics/PixelFormat.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
namespace lime {
|
||||
|
||||
|
||||
int __alpha16[0xFF + 1];
|
||||
int __clamp[0xFF + 0xFF + 1];
|
||||
static int a16;
|
||||
static double unmult;
|
||||
|
||||
int initValues () {
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
__alpha16[i] = ceil ((float)(i + 1) * ((1 << 16) / 0xFF));
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 0xFF; i++) {
|
||||
|
||||
__clamp[i] = i;
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0xFF; i < (0xFF + 0xFF + 1); i++) {
|
||||
|
||||
__clamp[i] = 0xFF;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int initValues_ = initValues ();
|
||||
|
||||
|
||||
struct RGBA {
|
||||
|
||||
|
||||
public:
|
||||
|
||||
inline RGBA () {
|
||||
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline RGBA (int32_t rgba) {
|
||||
|
||||
r = (rgba >> 24) & 0xFF;
|
||||
g = (rgba >> 16) & 0xFF;
|
||||
b = (rgba >> 8) & 0xFF;
|
||||
a = rgba & 0xFF;
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline RGBA (unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||
|
||||
Set (r, g, b, a);
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline int32_t Get () {
|
||||
|
||||
int32_t value = ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF);
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void MultiplyAlpha () {
|
||||
|
||||
if (a == 0) {
|
||||
|
||||
Set (0, 0, 0, 0);
|
||||
|
||||
} else if (a != 0xFF) {
|
||||
|
||||
a16 = __alpha16[a];
|
||||
Set ((r * a16) >> 16, (g * a16) >> 16, (b * a16) >> 16, a);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void UnmultiplyAlpha () {
|
||||
|
||||
if (a != 0 && a != 0xFF) {
|
||||
|
||||
unmult = 255.0 / a;
|
||||
Set (__clamp[(int)(r * unmult)], __clamp[(int)(g * unmult)], __clamp[(int)(b * unmult)], a);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void ReadUInt8 (const unsigned char* data, int offset, PixelFormat format, bool premultiplied) {
|
||||
|
||||
switch (format) {
|
||||
|
||||
case BGRA32:
|
||||
|
||||
Set (data[offset + 2], data[offset + 1], data[offset], data[offset + 3]);
|
||||
break;
|
||||
|
||||
case RGBA32:
|
||||
|
||||
Set (data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
|
||||
break;
|
||||
|
||||
case ARGB32:
|
||||
|
||||
Set (data[offset + 1], data[offset + 2], data[offset + 3], data[offset]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (premultiplied) {
|
||||
|
||||
UnmultiplyAlpha ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void Set (unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||
|
||||
this->r = r;
|
||||
this->g = g;
|
||||
this->b = b;
|
||||
this->a = a;
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void WriteUInt8 (unsigned char* data, int offset, PixelFormat format, bool premultiplied) {
|
||||
|
||||
if (premultiplied) {
|
||||
|
||||
MultiplyAlpha ();
|
||||
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
|
||||
case BGRA32:
|
||||
|
||||
data[offset] = b;
|
||||
data[offset + 1] = g;
|
||||
data[offset + 2] = r;
|
||||
data[offset + 3] = a;
|
||||
break;
|
||||
|
||||
case RGBA32:
|
||||
|
||||
data[offset] = r;
|
||||
data[offset + 1] = g;
|
||||
data[offset + 2] = b;
|
||||
data[offset + 3] = a;
|
||||
break;
|
||||
|
||||
case ARGB32:
|
||||
|
||||
data[offset] = a;
|
||||
data[offset + 1] = r;
|
||||
data[offset + 2] = g;
|
||||
data[offset + 3] = b;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline bool operator == (RGBA& rgba) {
|
||||
|
||||
return (a == rgba.a && r == rgba.r && g == rgba.g && b == rgba.b);
|
||||
|
||||
}
|
||||
|
||||
|
||||
unsigned char r;
|
||||
unsigned char g;
|
||||
unsigned char b;
|
||||
unsigned char a;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Submodule project/lib/curl updated: b5b913c014...7084474ab4
Submodule project/lib/png updated: 95d51b67a1...601ea3ce9b
@@ -127,6 +127,23 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
value lime_bytes_from_data_pointer (value data, value length) {
|
||||
|
||||
int size = val_int (length);
|
||||
intptr_t ptr = (intptr_t)val_float (data);
|
||||
Bytes bytes = Bytes (size);
|
||||
|
||||
if (ptr) {
|
||||
|
||||
memcpy (bytes.Data (), (const void*)ptr, size);
|
||||
|
||||
}
|
||||
|
||||
return bytes.Value ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_bytes_get_data_pointer (value bytes) {
|
||||
|
||||
Bytes data = Bytes (bytes);
|
||||
@@ -135,6 +152,14 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
value lime_bytes_read_file (value path) {
|
||||
|
||||
Bytes data = Bytes (val_os_string (path));
|
||||
return data.Value ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void lime_font_destroy (value handle) {
|
||||
|
||||
#ifdef LIME_FREETYPE
|
||||
@@ -508,32 +533,49 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
value lime_image_data_util_copy_pixels (value image, value sourceImage, value sourceRect, value destPoint, value mergeAlpha) {
|
||||
value lime_image_data_util_copy_pixels (value *arg, int nargs) {
|
||||
|
||||
enum { image, sourceImage, sourceRect, destPoint, alphaImage, alphaPoint, mergeAlpha };
|
||||
|
||||
Image _image = Image (arg[image]);
|
||||
Image _sourceImage = Image (arg[sourceImage]);
|
||||
Rectangle _sourceRect = Rectangle (arg[sourceRect]);
|
||||
Vector2 _destPoint = Vector2 (arg[destPoint]);
|
||||
|
||||
if (val_is_null (arg[alphaImage])) {
|
||||
|
||||
ImageDataUtil::CopyPixels (&_image, &_sourceImage, &_sourceRect, &_destPoint, 0, 0, val_bool (arg[mergeAlpha]));
|
||||
|
||||
} else {
|
||||
|
||||
Image _alphaImage = Image (arg[alphaImage]);
|
||||
Vector2 _alphaPoint = Vector2 (arg[alphaPoint]);
|
||||
|
||||
ImageDataUtil::CopyPixels (&_image, &_sourceImage, &_sourceRect, &_destPoint, &_alphaImage, &_alphaPoint, val_bool (arg[mergeAlpha]));
|
||||
|
||||
}
|
||||
|
||||
Image _image = Image (image);
|
||||
Image _sourceImage = Image (sourceImage);
|
||||
Rectangle _sourceRect = Rectangle (sourceRect);
|
||||
Vector2 _destPoint = Vector2 (destPoint);
|
||||
ImageDataUtil::CopyPixels (&_image, &_sourceImage, &_sourceRect, &_destPoint, val_bool (mergeAlpha));
|
||||
return alloc_null ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_image_data_util_fill_rect (value image, value rect, value color) {
|
||||
value lime_image_data_util_fill_rect (value image, value rect, value rg, value ba) {
|
||||
|
||||
Image _image = Image (image);
|
||||
Rectangle _rect = Rectangle (rect);
|
||||
ImageDataUtil::FillRect (&_image, &_rect, val_number (color));
|
||||
int32_t color = (val_int (rg) << 16) | val_int (ba);
|
||||
ImageDataUtil::FillRect (&_image, &_rect, color);
|
||||
return alloc_null ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_image_data_util_flood_fill (value image, value x, value y, value color) {
|
||||
value lime_image_data_util_flood_fill (value image, value x, value y, value rg, value ba) {
|
||||
|
||||
Image _image = Image (image);
|
||||
ImageDataUtil::FloodFill (&_image, val_number (x), val_number (y), val_number (color));
|
||||
int32_t color = (val_int (rg) << 16) | val_int (ba);
|
||||
ImageDataUtil::FloodFill (&_image, val_number (x), val_number (y), color);
|
||||
return alloc_null ();
|
||||
|
||||
}
|
||||
@@ -843,6 +885,14 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
value lime_renderer_get_type (value renderer) {
|
||||
|
||||
Renderer* targetRenderer = (Renderer*)(intptr_t)val_float (renderer);
|
||||
return alloc_string (targetRenderer->Type ());
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_renderer_lock (value renderer) {
|
||||
|
||||
return ((Renderer*)(intptr_t)val_float (renderer))->Lock ();
|
||||
@@ -1120,7 +1170,9 @@ namespace lime {
|
||||
DEFINE_PRIM (lime_application_set_frame_rate, 2);
|
||||
DEFINE_PRIM (lime_application_update, 1);
|
||||
DEFINE_PRIM (lime_audio_load, 1);
|
||||
DEFINE_PRIM (lime_bytes_from_data_pointer, 2);
|
||||
DEFINE_PRIM (lime_bytes_get_data_pointer, 1);
|
||||
DEFINE_PRIM (lime_bytes_read_file, 1);
|
||||
DEFINE_PRIM (lime_font_get_ascender, 1);
|
||||
DEFINE_PRIM (lime_font_get_descender, 1);
|
||||
DEFINE_PRIM (lime_font_get_family_name, 1);
|
||||
@@ -1143,9 +1195,9 @@ namespace lime {
|
||||
DEFINE_PRIM (lime_gamepad_get_device_name, 1);
|
||||
DEFINE_PRIM (lime_image_data_util_color_transform, 3);
|
||||
DEFINE_PRIM_MULT (lime_image_data_util_copy_channel);
|
||||
DEFINE_PRIM (lime_image_data_util_copy_pixels, 5);
|
||||
DEFINE_PRIM (lime_image_data_util_fill_rect, 3);
|
||||
DEFINE_PRIM (lime_image_data_util_flood_fill, 4);
|
||||
DEFINE_PRIM_MULT (lime_image_data_util_copy_pixels);
|
||||
DEFINE_PRIM (lime_image_data_util_fill_rect, 4);
|
||||
DEFINE_PRIM (lime_image_data_util_flood_fill, 5);
|
||||
DEFINE_PRIM (lime_image_data_util_get_pixels, 4);
|
||||
DEFINE_PRIM_MULT (lime_image_data_util_merge);
|
||||
DEFINE_PRIM (lime_image_data_util_multiply_alpha, 1);
|
||||
@@ -1172,6 +1224,7 @@ namespace lime {
|
||||
DEFINE_PRIM (lime_png_decode_file, 2);
|
||||
DEFINE_PRIM (lime_renderer_create, 1);
|
||||
DEFINE_PRIM (lime_renderer_flip, 1);
|
||||
DEFINE_PRIM (lime_renderer_get_type, 1);
|
||||
DEFINE_PRIM (lime_renderer_lock, 1);
|
||||
DEFINE_PRIM (lime_renderer_unlock, 1);
|
||||
DEFINE_PRIM (lime_render_event_manager_register, 2);
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace lime {
|
||||
|
||||
AutoGCRoot* Application::callback = 0;
|
||||
SDLApplication* SDLApplication::currentApplication = 0;
|
||||
|
||||
std::map<int, std::map<int, int> > gamepadsAxisMap;
|
||||
const int analogAxisDeadZone = 1000;
|
||||
|
||||
SDLApplication::SDLApplication () {
|
||||
|
||||
@@ -239,9 +240,36 @@ namespace lime {
|
||||
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
|
||||
if (gamepadsAxisMap[event->caxis.which].empty()) {
|
||||
|
||||
gamepadsAxisMap[event->caxis.which][event->caxis.axis] = event->caxis.value;
|
||||
|
||||
}
|
||||
else if (gamepadsAxisMap[event->caxis.which][event->caxis.axis] == event->caxis.value) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
gamepadEvent.type = AXIS_MOVE;
|
||||
gamepadEvent.axis = event->caxis.axis;
|
||||
gamepadEvent.id = event->caxis.which;
|
||||
|
||||
if (event->caxis.value > -analogAxisDeadZone && event->caxis.value < analogAxisDeadZone) {
|
||||
|
||||
if (gamepadsAxisMap[event->caxis.which][event->caxis.axis] != 0) {
|
||||
|
||||
gamepadsAxisMap[event->caxis.which][event->caxis.axis] = 0;
|
||||
gamepadEvent.axisValue = 0;
|
||||
GamepadEvent::Dispatch (&gamepadEvent);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
gamepadsAxisMap[event->caxis.which][event->caxis.axis] = event->caxis.value;
|
||||
gamepadEvent.axisValue = event->caxis.value / 32768.0;
|
||||
|
||||
GamepadEvent::Dispatch (&gamepadEvent);
|
||||
|
||||
@@ -31,6 +31,15 @@ namespace lime {
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer (sdlWindow, -1, sdlFlags);
|
||||
|
||||
if (!sdlRenderer && (sdlFlags & SDL_RENDERER_ACCELERATED)) {
|
||||
|
||||
sdlFlags &= ~SDL_RENDERER_ACCELERATED;
|
||||
sdlFlags |= SDL_RENDERER_SOFTWARE;
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer (sdlWindow, -1, sdlFlags);
|
||||
|
||||
}
|
||||
|
||||
if (!sdlRenderer) {
|
||||
|
||||
printf ("Could not create SDL renderer: %s.\n", SDL_GetError ());
|
||||
@@ -95,6 +104,30 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
const char* SDLRenderer::Type () {
|
||||
|
||||
if (sdlRenderer) {
|
||||
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRendererInfo (sdlRenderer, &info);
|
||||
|
||||
if (info.flags & SDL_RENDERER_SOFTWARE) {
|
||||
|
||||
return "software";
|
||||
|
||||
} else {
|
||||
|
||||
return "opengl";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return "none";
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SDLRenderer::Unlock () {
|
||||
|
||||
if (sdlTexture) {
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace lime {
|
||||
|
||||
virtual void Flip ();
|
||||
virtual value Lock ();
|
||||
virtual const char* Type ();
|
||||
virtual void Unlock ();
|
||||
|
||||
SDL_Renderer* sdlRenderer;
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace lime {
|
||||
static int id_height;
|
||||
static int id_offsetX;
|
||||
static int id_offsetY;
|
||||
static int id_transparent;
|
||||
static int id_width;
|
||||
static bool init = false;
|
||||
|
||||
@@ -19,7 +18,6 @@ namespace lime {
|
||||
height = 0;
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
transparent = true;
|
||||
width = 0;
|
||||
|
||||
}
|
||||
@@ -33,7 +31,6 @@ namespace lime {
|
||||
id_height = val_id ("height");
|
||||
id_offsetX = val_id ("offsetX");
|
||||
id_offsetY = val_id ("offsetY");
|
||||
id_transparent = val_id ("transparent");
|
||||
id_width = val_id ("width");
|
||||
init = true;
|
||||
|
||||
@@ -44,7 +41,6 @@ namespace lime {
|
||||
buffer = new ImageBuffer (val_field (image, id_buffer));
|
||||
offsetX = val_int (val_field (image, id_offsetX));
|
||||
offsetY = val_int (val_field (image, id_offsetY));
|
||||
transparent = val_bool (val_field (image, id_transparent));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ namespace lime {
|
||||
static int id_data;
|
||||
static int id_format;
|
||||
static int id_height;
|
||||
static int id_width;
|
||||
static int id_premultiplied;
|
||||
static int id_transparent;
|
||||
static int id_width;
|
||||
static bool init = false;
|
||||
|
||||
|
||||
@@ -19,8 +20,9 @@ namespace lime {
|
||||
width = 0;
|
||||
height = 0;
|
||||
bitsPerPixel = 32;
|
||||
format = RGBA;
|
||||
format = RGBA32;
|
||||
data = 0;
|
||||
premultiplied = false;
|
||||
transparent = false;
|
||||
|
||||
}
|
||||
@@ -37,6 +39,7 @@ namespace lime {
|
||||
id_height = val_id ("height");
|
||||
id_data = val_id ("data");
|
||||
id_format = val_id ("format");
|
||||
id_premultiplied = val_id ("premultiplied");
|
||||
init = true;
|
||||
|
||||
}
|
||||
@@ -48,6 +51,7 @@ namespace lime {
|
||||
transparent = val_bool (val_field (imageBuffer, id_transparent));
|
||||
value data_value = val_field (imageBuffer, id_data);
|
||||
value buffer_value = val_field (data_value, id_buffer);
|
||||
premultiplied = val_bool (val_field (imageBuffer, id_premultiplied));
|
||||
data = new Bytes (buffer_value);
|
||||
|
||||
}
|
||||
@@ -119,6 +123,7 @@ namespace lime {
|
||||
id_height = val_id ("height");
|
||||
id_data = val_id ("data");
|
||||
id_format = val_id ("format");
|
||||
id_premultiplied = val_id ("premultiplied");
|
||||
init = true;
|
||||
|
||||
}
|
||||
@@ -130,6 +135,7 @@ namespace lime {
|
||||
alloc_field (mValue, id_data, data ? data->Value () : alloc_null ());
|
||||
alloc_field (mValue, id_transparent, alloc_bool (transparent));
|
||||
alloc_field (mValue, id_format, alloc_int (format));
|
||||
alloc_field (mValue, id_premultiplied, alloc_bool (premultiplied));
|
||||
return mValue;
|
||||
|
||||
}
|
||||
|
||||
@@ -368,16 +368,37 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
value lime_cairo_image_surface_get_data (value handle) {
|
||||
|
||||
return alloc_float ((intptr_t)cairo_image_surface_get_data ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_cairo_image_surface_get_format (value handle) {
|
||||
|
||||
return alloc_int ((int)cairo_image_surface_get_format ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_cairo_image_surface_get_height (value handle) {
|
||||
|
||||
return alloc_int ((intptr_t)cairo_image_surface_get_height ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
return alloc_int (cairo_image_surface_get_height ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_cairo_image_surface_get_stride (value handle) {
|
||||
|
||||
return alloc_int (cairo_image_surface_get_stride ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
value lime_cairo_image_surface_get_width (value handle) {
|
||||
|
||||
return alloc_int ((intptr_t)cairo_image_surface_get_width ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
return alloc_int (cairo_image_surface_get_width ((cairo_surface_t*)(intptr_t)val_float (handle)));
|
||||
|
||||
}
|
||||
|
||||
@@ -993,7 +1014,10 @@ namespace lime {
|
||||
DEFINE_PRIM (lime_cairo_identity_matrix, 1);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_create, 3);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_create_for_data, 5);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_get_data, 1);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_get_format, 1);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_get_height, 1);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_get_stride, 1);
|
||||
DEFINE_PRIM (lime_cairo_image_surface_get_width, 1);
|
||||
DEFINE_PRIM (lime_cairo_in_clip, 3);
|
||||
DEFINE_PRIM (lime_cairo_in_fill, 3);
|
||||
|
||||
@@ -1,82 +1,45 @@
|
||||
#include <graphics/utils/ImageDataUtil.h>
|
||||
#include <math/color/RGBA.h>
|
||||
#include <system/System.h>
|
||||
#include <utils/QuickVec.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
namespace lime {
|
||||
|
||||
|
||||
static int __alpha16[0xFF + 1];
|
||||
static int __clamp[0xFF + 0xFF + 1];
|
||||
|
||||
int initValues () {
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// Seem to need +1 to get the same results as Haxe in multiplyAlpha
|
||||
__alpha16[i] = (i + 1) * ((1 << 16) / 0xFF);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 0xFF; i++) {
|
||||
|
||||
__clamp[i] = i;
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0xFF; i < (0xFF + 0xFF + 1); i++) {
|
||||
|
||||
__clamp[i] = 0xFF;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int initValues_ = initValues ();
|
||||
|
||||
|
||||
void ImageDataUtil::ColorTransform (Image* image, Rectangle* rect, ColorMatrix* colorMatrix) {
|
||||
|
||||
int stride = image->buffer->Stride ();
|
||||
int offset;
|
||||
|
||||
int rowStart = int (rect->y + image->offsetY);
|
||||
int rowEnd = int (rect->y + rect->height + image->offsetY);
|
||||
int columnStart = int (rect->x + image->offsetX);
|
||||
int columnEnd = int (rect->x + rect->width + image->offsetX);
|
||||
|
||||
PixelFormat format = image->buffer->format;
|
||||
bool premultiplied = image->buffer->premultiplied;
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
int r, g, b, a, ex = 0;
|
||||
|
||||
float alphaMultiplier = colorMatrix->GetAlphaMultiplier ();
|
||||
float redMultiplier = colorMatrix->GetRedMultiplier ();
|
||||
float greenMultiplier = colorMatrix->GetGreenMultiplier ();
|
||||
float blueMultiplier = colorMatrix->GetBlueMultiplier ();
|
||||
int alphaOffset = colorMatrix->GetAlphaOffset ();
|
||||
int redOffset = colorMatrix->GetRedOffset ();
|
||||
int greenOffset = colorMatrix->GetGreenOffset ();
|
||||
int blueOffset = colorMatrix->GetBlueOffset ();
|
||||
ImageDataView dataView = ImageDataView (image, rect);
|
||||
|
||||
for (int row = rowStart; row < rowEnd; row++) {
|
||||
unsigned char alphaTable[256];
|
||||
unsigned char redTable[256];
|
||||
unsigned char greenTable[256];
|
||||
unsigned char blueTable[256];
|
||||
|
||||
colorMatrix->GetAlphaTable (alphaTable);
|
||||
colorMatrix->GetRedTable (redTable);
|
||||
colorMatrix->GetGreenTable (greenTable);
|
||||
colorMatrix->GetBlueTable (blueTable);
|
||||
|
||||
int row, offset;
|
||||
RGBA pixel;
|
||||
|
||||
for (int y = 0; y < dataView.height; y++) {
|
||||
|
||||
for (int column = columnStart; column < columnEnd; column++) {
|
||||
row = dataView.Row (y);
|
||||
|
||||
for (int x = 0; x < dataView.width; x++) {
|
||||
|
||||
offset = (row * stride) + (column * 4);
|
||||
offset = row + (x * 4);
|
||||
|
||||
a = (data[offset + 3] * alphaMultiplier) + alphaOffset;
|
||||
ex = a > 0xFF ? a - 0xFF : 0;
|
||||
b = (data[offset + 2] * blueMultiplier) + blueOffset + ex;
|
||||
ex = b > 0xFF ? b - 0xFF : 0;
|
||||
g = (data[offset + 1] * greenMultiplier) + greenOffset + ex;
|
||||
ex = g > 0xFF ? g - 0xFF : 0;
|
||||
r = (data[offset] * redMultiplier) + redOffset + ex;
|
||||
|
||||
data[offset] = r > 0xFF ? 0xFF : r;
|
||||
data[offset + 1] = g > 0xFF ? 0xFF : g;
|
||||
data[offset + 2] = b > 0xFF ? 0xFF : b;
|
||||
data[offset + 3] = a > 0xFF ? 0xFF : a;
|
||||
pixel.ReadUInt8 (data, offset, format, premultiplied);
|
||||
pixel.Set (redTable[pixel.r], greenTable[pixel.g], blueTable[pixel.b], alphaTable[pixel.a]);
|
||||
pixel.WriteUInt8 (data, offset, format, premultiplied);
|
||||
|
||||
}
|
||||
|
||||
@@ -87,36 +50,54 @@ namespace lime {
|
||||
|
||||
void ImageDataUtil::CopyChannel (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int srcChannel, int destChannel) {
|
||||
|
||||
int srcStride = sourceImage->buffer->Stride ();
|
||||
int srcPosition = ((sourceRect->x + sourceImage->offsetX) * 4) + (srcStride * (sourceRect->y + sourceImage->offsetY)) + srcChannel;
|
||||
int srcRowOffset = srcStride - int (4 * (sourceRect->width + sourceImage->offsetX));
|
||||
int srcRowEnd = 4 * (sourceRect->x + sourceImage->offsetX + sourceRect->width);
|
||||
uint8_t* srcData = (uint8_t*)sourceImage->buffer->data->Data ();
|
||||
|
||||
int destStride = image->buffer->Stride ();
|
||||
int destPosition = ((destPoint->x + image->offsetX) * 4) + (destStride * (destPoint->y + image->offsetY)) + destChannel;
|
||||
int destRowOffset = destStride - int (4 * (sourceRect->width + image->offsetX));
|
||||
int destRowEnd = 4 * (destPoint->x + image->offsetX + sourceRect->width);
|
||||
uint8_t* destData = (uint8_t*)image->buffer->data->Data ();
|
||||
|
||||
int length = sourceRect->width * sourceRect->height;
|
||||
ImageDataView srcView = ImageDataView (sourceImage, sourceRect);
|
||||
Rectangle destRect = Rectangle (destPoint->x, destPoint->y, srcView.width, srcView.height);
|
||||
ImageDataView destView = ImageDataView (image, &destRect);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
PixelFormat srcFormat = sourceImage->buffer->format;
|
||||
PixelFormat destFormat = image->buffer->format;
|
||||
bool srcPremultiplied = sourceImage->buffer->premultiplied;
|
||||
bool destPremultiplied = image->buffer->premultiplied;
|
||||
|
||||
int srcPosition, destPosition;
|
||||
RGBA srcPixel, destPixel;
|
||||
unsigned char value = 0;
|
||||
|
||||
for (int y = 0; y < destView.height; y++) {
|
||||
|
||||
destData[destPosition] = srcData[srcPosition];
|
||||
srcPosition = srcView.Row (y);
|
||||
destPosition = destView.Row (y);
|
||||
|
||||
srcPosition += 4;
|
||||
destPosition += 4;
|
||||
|
||||
if ((srcPosition % srcStride) > srcRowEnd) {
|
||||
for (int x = 0; x < destView.width; x++) {
|
||||
|
||||
srcPosition += srcRowOffset;
|
||||
srcPixel.ReadUInt8 (srcData, srcPosition, srcFormat, srcPremultiplied);
|
||||
destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
}
|
||||
|
||||
if ((destPosition % destStride) > destRowEnd) {
|
||||
switch (srcChannel) {
|
||||
|
||||
case 0: value = srcPixel.r; break;
|
||||
case 1: value = srcPixel.g; break;
|
||||
case 2: value = srcPixel.b; break;
|
||||
case 3: value = srcPixel.a; break;
|
||||
|
||||
}
|
||||
|
||||
destPosition += destRowOffset;
|
||||
switch (destChannel) {
|
||||
|
||||
case 0: destPixel.r = value; break;
|
||||
case 1: destPixel.g = value; break;
|
||||
case 2: destPixel.b = value; break;
|
||||
case 3: destPixel.a = value; break;
|
||||
|
||||
}
|
||||
|
||||
destPixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
srcPosition += 4;
|
||||
destPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
@@ -125,35 +106,37 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
void ImageDataUtil::CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, bool mergeAlpha) {
|
||||
|
||||
int rowOffset = int (destPoint->y + image->offsetY - sourceRect->y - sourceImage->offsetY);
|
||||
int columnOffset = int (destPoint->x + image->offsetX - sourceRect->x - sourceImage->offsetY);
|
||||
void ImageDataUtil::CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, Image* alphaImage, Vector2* alphaPoint, bool mergeAlpha) {
|
||||
|
||||
uint8_t* sourceData = (uint8_t*)sourceImage->buffer->data->Data ();
|
||||
int sourceStride = sourceImage->buffer->Stride ();
|
||||
int sourceOffset = 0;
|
||||
uint8_t* destData = (uint8_t*)image->buffer->data->Data ();
|
||||
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
int stride = image->buffer->Stride ();
|
||||
int offset = 0;
|
||||
ImageDataView sourceView = ImageDataView (sourceImage, sourceRect);
|
||||
Rectangle destRect = Rectangle (destPoint->x, destPoint->y, sourceView.width, sourceView.height);
|
||||
ImageDataView destView = ImageDataView (image, &destRect);
|
||||
|
||||
int rows = sourceRect->y + sourceRect->height + sourceImage->offsetY;
|
||||
int columns = sourceRect->x + sourceRect->width + sourceImage->offsetX;
|
||||
PixelFormat sourceFormat = sourceImage->buffer->format;
|
||||
PixelFormat destFormat = image->buffer->format;
|
||||
bool sourcePremultiplied = sourceImage->buffer->premultiplied;
|
||||
bool destPremultiplied = image->buffer->premultiplied;
|
||||
|
||||
int sourcePosition, destPosition;
|
||||
RGBA sourcePixel;
|
||||
|
||||
if (!mergeAlpha || !sourceImage->buffer->transparent) {
|
||||
|
||||
for (int row = sourceRect->y + sourceImage->offsetY; row < rows; row++) {
|
||||
for (int y = 0; y < destView.height; y++) {
|
||||
|
||||
for (int column = sourceRect->x + sourceImage->offsetX; column < columns; column++) {
|
||||
sourcePosition = sourceView.Row (y);
|
||||
destPosition = destView.Row (y);
|
||||
|
||||
for (int x = 0; x < destView.width; x++) {
|
||||
|
||||
sourceOffset = (row * sourceStride) + (column * 4);
|
||||
offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4);
|
||||
sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied);
|
||||
sourcePixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
data[offset] = sourceData[sourceOffset];
|
||||
data[offset + 1] = sourceData[sourceOffset + 1];
|
||||
data[offset + 2] = sourceData[sourceOffset + 2];
|
||||
data[offset + 3] = sourceData[sourceOffset + 3];
|
||||
sourcePosition += 4;
|
||||
destPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
@@ -161,75 +144,95 @@ namespace lime {
|
||||
|
||||
} else {
|
||||
|
||||
float sourceAlpha;
|
||||
float destAlpha;
|
||||
float outA;
|
||||
float oneMinusSourceAlpha;
|
||||
float sourceAlpha, destAlpha, oneMinusSourceAlpha, blendAlpha;
|
||||
RGBA destPixel;
|
||||
|
||||
for (int row = sourceRect->y + sourceImage->offsetY; row < rows; row++) {
|
||||
if (alphaImage == 0) {
|
||||
|
||||
for (int column = sourceRect->x + sourceImage->offsetX; column < columns; column++) {
|
||||
for (int y = 0; y < destView.height; y++) {
|
||||
|
||||
sourceOffset = (row * sourceStride) + (column * 4);
|
||||
offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4);
|
||||
sourcePosition = sourceView.Row (y);
|
||||
destPosition = destView.Row (y);
|
||||
|
||||
sourceAlpha = sourceData[sourceOffset + 3] / 255.0;
|
||||
destAlpha = data[offset + 3] / 255.0;
|
||||
oneMinusSourceAlpha = (1 - sourceAlpha);
|
||||
|
||||
outA = sourceAlpha + destAlpha * oneMinusSourceAlpha;
|
||||
data[offset + 0] = __clamp[int (0.5 + ((sourceData[sourceOffset + 0] * sourceAlpha + data[offset + 0] * destAlpha * oneMinusSourceAlpha) / outA))];
|
||||
data[offset + 1] = __clamp[int (0.5 + ((sourceData[sourceOffset + 1] * sourceAlpha + data[offset + 1] * destAlpha * oneMinusSourceAlpha) / outA))];
|
||||
data[offset + 2] = __clamp[int (0.5 + ((sourceData[sourceOffset + 2] * sourceAlpha + data[offset + 2] * destAlpha * oneMinusSourceAlpha) / outA))];
|
||||
data[offset + 3] = __clamp[int (0.5 + (outA * 255.0))];
|
||||
for (int x = 0; x < destView.width; x++) {
|
||||
|
||||
sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied);
|
||||
destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
sourceAlpha = sourcePixel.a / 255.0;
|
||||
destAlpha = destPixel.a / 255.0;
|
||||
oneMinusSourceAlpha = 1 - sourceAlpha;
|
||||
blendAlpha = sourceAlpha + (destAlpha * oneMinusSourceAlpha);
|
||||
|
||||
if (blendAlpha == 0) {
|
||||
|
||||
destPixel.Set (0, 0, 0, 0);
|
||||
|
||||
} else {
|
||||
|
||||
destPixel.r = __clamp[int (0.5 + (sourcePixel.r * sourceAlpha + destPixel.r * destAlpha * oneMinusSourceAlpha) / blendAlpha)];
|
||||
destPixel.g = __clamp[int (0.5 + (sourcePixel.g * sourceAlpha + destPixel.g * destAlpha * oneMinusSourceAlpha) / blendAlpha)];
|
||||
destPixel.b = __clamp[int (0.5 + (sourcePixel.b * sourceAlpha + destPixel.b * destAlpha * oneMinusSourceAlpha) / blendAlpha)];
|
||||
destPixel.a = __clamp[int (0.5 + blendAlpha * 255.0)];
|
||||
|
||||
}
|
||||
|
||||
destPixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
sourcePosition += 4;
|
||||
destPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ImageDataUtil::FillRect (Image* image, Rectangle* rect, int color) {
|
||||
|
||||
int* data = (int*)image->buffer->data->Data ();
|
||||
|
||||
if (rect->width == image->buffer->width && rect->height == image->buffer->height && rect->x == 0 && rect->y == 0 && image->offsetX == 0 && image->offsetY == 0) {
|
||||
|
||||
int length = image->buffer->width * image->buffer->height;
|
||||
|
||||
if (color == 0 || color == 0xFFFFFFFF || (color & 0xFF == (color >> 8) & 0xFF && (color >> 8) & 0xFF == (color >> 16) & 0xFF && (color >> 16) & 0xFF == (color >> 24) & 0xFF)) {
|
||||
|
||||
memset ((uint8_t*)data, color & 0xFF, length * 4);
|
||||
|
||||
} else {
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
data[i] = color;
|
||||
|
||||
}
|
||||
uint8_t* alphaData = (uint8_t*)alphaImage->buffer->data->Data ();
|
||||
PixelFormat alphaFormat = alphaImage->buffer->format;
|
||||
bool alphaPremultiplied = alphaImage->buffer->premultiplied;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int stride = image->buffer->width;
|
||||
int offset;
|
||||
|
||||
int rowStart = int (rect->y + image->offsetY);
|
||||
int rowEnd = int (rect->y + rect->height + image->offsetY);
|
||||
int columnStart = int (rect->x + image->offsetX);
|
||||
int columnEnd = int (rect->x + rect->width + image->offsetX);
|
||||
|
||||
for (int row = rowStart; row < rowEnd; row++) {
|
||||
Rectangle alphaRect = Rectangle (alphaPoint->x, alphaPoint->y, destView.width, destView.height);
|
||||
ImageDataView alphaView = ImageDataView (alphaImage, &alphaRect);
|
||||
int alphaPosition;
|
||||
RGBA alphaPixel;
|
||||
|
||||
for (int column = columnStart; column < columnEnd; column++) {
|
||||
for (int y = 0; y < alphaView.height; y++) {
|
||||
|
||||
offset = (row * stride) + (column);
|
||||
data[offset] = color;
|
||||
sourcePosition = sourceView.Row (y);
|
||||
destPosition = destView.Row (y);
|
||||
alphaPosition = alphaView.Row (y);
|
||||
|
||||
for (int x = 0; x < alphaView.width; x++) {
|
||||
|
||||
sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied);
|
||||
destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
alphaPixel.ReadUInt8 (alphaData, alphaPosition, alphaFormat, alphaPremultiplied);
|
||||
|
||||
sourceAlpha = alphaPixel.a / 0xFF;
|
||||
destAlpha = destPixel.a / 0xFF;
|
||||
oneMinusSourceAlpha = 1 - sourceAlpha;
|
||||
blendAlpha = sourceAlpha + (destAlpha * oneMinusSourceAlpha);
|
||||
|
||||
if (blendAlpha == 0) {
|
||||
|
||||
destPixel.Set (0, 0, 0, 0);
|
||||
|
||||
} else {
|
||||
|
||||
destPixel.r = __clamp[int (0.5 + (sourcePixel.r * sourceAlpha + destPixel.r * destAlpha * oneMinusSourceAlpha) / blendAlpha)];
|
||||
destPixel.g = __clamp[int (0.5 + (sourcePixel.g * sourceAlpha + destPixel.g * destAlpha * oneMinusSourceAlpha) / blendAlpha)];
|
||||
destPixel.b = __clamp[int (0.5 + (sourcePixel.b * sourceAlpha + destPixel.b * destAlpha * oneMinusSourceAlpha) / blendAlpha)];
|
||||
destPixel.a = __clamp[int (0.5 + blendAlpha * 255.0)];
|
||||
|
||||
}
|
||||
|
||||
destPixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
sourcePosition += 4;
|
||||
destPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -240,22 +243,50 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
void ImageDataUtil::FloodFill (Image* image, int x, int y, int color) {
|
||||
void ImageDataUtil::FillRect (Image* image, Rectangle* rect, int32_t color) {
|
||||
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
PixelFormat format = image->buffer->format;
|
||||
bool premultiplied = image->buffer->premultiplied;
|
||||
|
||||
int offset = (((y + image->offsetY) * (image->buffer->width * 4)) + ((x + image->offsetX) * 4));
|
||||
uint8_t hitColorR = data[offset + 0];
|
||||
uint8_t hitColorG = data[offset + 1];
|
||||
uint8_t hitColorB = data[offset + 2];
|
||||
uint8_t hitColorA = image->transparent ? data[offset + 3] : 0xFF;
|
||||
ImageDataView dataView = ImageDataView (image, rect);
|
||||
int row;
|
||||
RGBA fillColor (color);
|
||||
|
||||
uint8_t r = (color >> 24) & 0xFF;
|
||||
uint8_t g = (color >> 16) & 0xFF;
|
||||
uint8_t b = (color >> 8) & 0xFF;
|
||||
uint8_t a = image->transparent ? color & 0xFF : 0xFF;
|
||||
for (int y = 0; y < dataView.height; y++) {
|
||||
|
||||
row = dataView.Row (y);
|
||||
|
||||
for (int x = 0; x < dataView.width; x++) {
|
||||
|
||||
fillColor.WriteUInt8 (data, row + (x * 4), format, premultiplied);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hitColorR == r && hitColorG == g && hitColorB == b && hitColorA == a) return;
|
||||
}
|
||||
|
||||
|
||||
void ImageDataUtil::FloodFill (Image* image, int x, int y, int32_t color) {
|
||||
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
PixelFormat format = image->buffer->format;
|
||||
bool premultiplied = image->buffer->premultiplied;
|
||||
|
||||
RGBA fillColor (color);
|
||||
|
||||
RGBA hitColor;
|
||||
hitColor.ReadUInt8 (data, ((y + image->offsetY) * (image->buffer->width * 4)) + ((x + image->offsetX) * 4), format, premultiplied);
|
||||
|
||||
if (!image->buffer->transparent) {
|
||||
|
||||
fillColor.a = 0xFF;
|
||||
hitColor.a = 0xFF;
|
||||
|
||||
}
|
||||
|
||||
if (fillColor == hitColor) return;
|
||||
|
||||
int dx[4] = { 0, -1, 1, 0 };
|
||||
int dy[4] = { -1, 0, 0, 1 };
|
||||
@@ -270,6 +301,7 @@ namespace lime {
|
||||
queue.push_back (y);
|
||||
|
||||
int curPointX, curPointY, i, nextPointX, nextPointY, nextPointOffset;
|
||||
RGBA readColor;
|
||||
|
||||
while (queue.size () > 0) {
|
||||
|
||||
@@ -288,13 +320,11 @@ namespace lime {
|
||||
}
|
||||
|
||||
nextPointOffset = (nextPointY * image->width + nextPointX) * 4;
|
||||
readColor.ReadUInt8 (data, nextPointOffset, format, premultiplied);
|
||||
|
||||
if (data[nextPointOffset + 0] == hitColorR && data[nextPointOffset + 1] == hitColorG && data[nextPointOffset + 2] == hitColorB && data[nextPointOffset + 3] == hitColorA) {
|
||||
if (readColor == hitColor) {
|
||||
|
||||
data[nextPointOffset + 0] = r;
|
||||
data[nextPointOffset + 1] = g;
|
||||
data[nextPointOffset + 2] = b;
|
||||
data[nextPointOffset + 3] = a;
|
||||
fillColor.WriteUInt8 (data, nextPointOffset, format, premultiplied);
|
||||
|
||||
queue.push_back (nextPointX);
|
||||
queue.push_back (nextPointY);
|
||||
@@ -313,52 +343,27 @@ namespace lime {
|
||||
int length = int (rect->width * rect->height);
|
||||
pixels->Resize (length * 4);
|
||||
|
||||
if (format == RGBA && rect->width == image->buffer->width && rect->height == image->buffer->height && rect->x == 0 && rect->y == 0) {
|
||||
|
||||
memcpy (pixels->Data (), image->buffer->data->Data (), image->buffer->data->Length ());
|
||||
return;
|
||||
|
||||
}
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
uint8_t* destData = (uint8_t*)pixels->Data ();
|
||||
|
||||
uint8_t* data = (uint8_t*)pixels->Data ();
|
||||
uint8_t* srcData = (uint8_t*)image->buffer->data->Data ();
|
||||
PixelFormat sourceFormat = image->buffer->format;
|
||||
bool premultiplied = image->buffer->premultiplied;
|
||||
|
||||
int srcStride = image->buffer->Stride ();
|
||||
int srcPosition = int ((rect->x * 4) + (srcStride * rect->y));
|
||||
int srcRowOffset = srcStride - int (4 * rect->width);
|
||||
int srcRowEnd = int (4 * (rect->x + rect->width));
|
||||
ImageDataView dataView = ImageDataView (image, rect);
|
||||
int position, destPosition = 0;
|
||||
RGBA pixel;
|
||||
|
||||
if (format == ARGB) {
|
||||
for (int y = 0; y < dataView.height; y++) {
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
data[i * 4 + 1] = srcData[srcPosition++];
|
||||
data[i * 4 + 2] = srcData[srcPosition++];
|
||||
data[i * 4 + 3] = srcData[srcPosition++];
|
||||
data[i * 4] = srcData[srcPosition++];
|
||||
|
||||
if ((srcPosition % srcStride) > srcRowEnd) {
|
||||
|
||||
srcPosition += srcRowOffset;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
position = dataView.Row (y);
|
||||
|
||||
} else {
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
for (int x = 0; x < dataView.width; x++) {
|
||||
|
||||
data[i * 4] = srcData[srcPosition++];
|
||||
data[i * 4 + 1] = srcData[srcPosition++];
|
||||
data[i * 4 + 2] = srcData[srcPosition++];
|
||||
data[i * 4 + 3] = srcData[srcPosition++];
|
||||
pixel.ReadUInt8 (data, position, sourceFormat, premultiplied);
|
||||
pixel.WriteUInt8 (destData, destPosition, format, false);
|
||||
|
||||
if ((srcPosition % srcStride) > srcRowEnd) {
|
||||
|
||||
srcPosition += srcRowOffset;
|
||||
|
||||
}
|
||||
position += 4;
|
||||
destPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
@@ -369,31 +374,39 @@ namespace lime {
|
||||
|
||||
void ImageDataUtil::Merge (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int redMultiplier, int greenMultiplier, int blueMultiplier, int alphaMultiplier) {
|
||||
|
||||
int rowOffset = int (destPoint->y + image->offsetY - sourceRect->y - sourceImage->offsetY);
|
||||
int columnOffset = int (destPoint->x + image->offsetX - sourceRect->x - sourceImage->offsetY);
|
||||
ImageDataView sourceView = ImageDataView (sourceImage, sourceRect);
|
||||
Rectangle destRect = Rectangle (destPoint->x, destPoint->y, sourceView.width, sourceView.height);
|
||||
ImageDataView destView = ImageDataView (image, &destRect);
|
||||
|
||||
uint8_t* sourceData = (uint8_t*)sourceImage->buffer->data->Data ();
|
||||
int sourceStride = sourceImage->buffer->Stride ();
|
||||
int sourceOffset = 0;
|
||||
uint8_t* destData = (uint8_t*)image->buffer->data->Data ();
|
||||
PixelFormat sourceFormat = sourceImage->buffer->format;
|
||||
PixelFormat destFormat = image->buffer->format;
|
||||
bool sourcePremultiplied = sourceImage->buffer->premultiplied;
|
||||
bool destPremultiplied = image->buffer->premultiplied;
|
||||
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
int stride = image->buffer->Stride ();
|
||||
int offset = 0;
|
||||
int sourcePosition, destPosition;
|
||||
RGBA sourcePixel, destPixel;
|
||||
|
||||
int rowEnd = int (sourceRect->y + sourceRect->height + sourceImage->offsetY);
|
||||
int columnEnd = int (sourceRect->x + sourceRect->width + sourceImage->offsetX);
|
||||
|
||||
for (int row = int (sourceRect->y + sourceImage->offsetY); row < rowEnd; row++) {
|
||||
for (int y = 0; y < destView.height; y++) {
|
||||
|
||||
for (int column = int (sourceRect->x + sourceImage->offsetX); column < columnEnd; column++) {
|
||||
sourcePosition = sourceView.Row (y);
|
||||
destPosition = destView.Row (y);
|
||||
|
||||
for (int x = 0; x < destView.width; x++) {
|
||||
|
||||
sourceOffset = (row * sourceStride) + (column * 4);
|
||||
offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4);
|
||||
sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied);
|
||||
destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
data[offset] = int (((sourceData[offset] * redMultiplier) + (data[offset] * (256 - redMultiplier))) / 256);
|
||||
data[offset + 1] = int (((sourceData[offset + 1] * greenMultiplier) + (data[offset + 1] * (256 - greenMultiplier))) / 256);
|
||||
data[offset + 2] = int (((sourceData[offset + 2] * blueMultiplier) + (data[offset + 2] * (256 - blueMultiplier))) / 256);
|
||||
data[offset + 3] = int (((sourceData[offset + 3] * alphaMultiplier) + (data[offset + 3] * (256 - alphaMultiplier))) / 256);
|
||||
destPixel.r = int (((sourcePixel.r * redMultiplier) + (destPixel.r * (256 - redMultiplier))) / 256);
|
||||
destPixel.g = int (((sourcePixel.g * greenMultiplier) + (destPixel.g * (256 - greenMultiplier))) / 256);
|
||||
destPixel.b = int (((sourcePixel.b * blueMultiplier) + (destPixel.b * (256 - blueMultiplier))) / 256);
|
||||
destPixel.a = int (((sourcePixel.a * alphaMultiplier) + (destPixel.a * (256 - alphaMultiplier))) / 256);
|
||||
|
||||
destPixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied);
|
||||
|
||||
sourcePosition += 4;
|
||||
destPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
@@ -404,17 +417,15 @@ namespace lime {
|
||||
|
||||
void ImageDataUtil::MultiplyAlpha (Image* image) {
|
||||
|
||||
int a16 = 0;
|
||||
int length = image->buffer->data->Length () / 4;
|
||||
PixelFormat format = image->buffer->format;
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
int length = int (image->buffer->data->Length () / 4);
|
||||
RGBA pixel;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
a16 = __alpha16[data[3]];
|
||||
data[0] = (data[0] * a16) >> 16;
|
||||
data[1] = (data[1] * a16) >> 16;
|
||||
data[2] = (data[2] * a16) >> 16;
|
||||
data += 4;
|
||||
pixel.ReadUInt8 (data, i * 4, format, false);
|
||||
pixel.WriteUInt8 (data, i * 4, format, true);
|
||||
|
||||
}
|
||||
|
||||
@@ -487,7 +498,7 @@ namespace lime {
|
||||
|
||||
switch (image->buffer->format) {
|
||||
|
||||
case RGBA:
|
||||
case RGBA32:
|
||||
|
||||
r1 = 0;
|
||||
g1 = 1;
|
||||
@@ -495,7 +506,7 @@ namespace lime {
|
||||
a1 = 3;
|
||||
break;
|
||||
|
||||
case ARGB:
|
||||
case ARGB32:
|
||||
|
||||
r1 = 1;
|
||||
g1 = 2;
|
||||
@@ -503,7 +514,7 @@ namespace lime {
|
||||
a1 = 0;
|
||||
break;
|
||||
|
||||
case BGRA:
|
||||
case BGRA32:
|
||||
|
||||
r1 = 2;
|
||||
g1 = 1;
|
||||
@@ -515,7 +526,7 @@ namespace lime {
|
||||
|
||||
switch (format) {
|
||||
|
||||
case RGBA:
|
||||
case RGBA32:
|
||||
|
||||
r2 = 0;
|
||||
g2 = 1;
|
||||
@@ -523,7 +534,7 @@ namespace lime {
|
||||
a2 = 3;
|
||||
break;
|
||||
|
||||
case ARGB:
|
||||
case ARGB32:
|
||||
|
||||
r2 = 1;
|
||||
g2 = 2;
|
||||
@@ -531,7 +542,7 @@ namespace lime {
|
||||
a2 = 0;
|
||||
break;
|
||||
|
||||
case BGRA:
|
||||
case BGRA32:
|
||||
|
||||
r2 = 2;
|
||||
g2 = 1;
|
||||
@@ -564,61 +575,29 @@ namespace lime {
|
||||
|
||||
void ImageDataUtil::SetPixels (Image* image, Rectangle* rect, Bytes* bytes, PixelFormat format) {
|
||||
|
||||
if (format == RGBA && rect->width == image->buffer->width && rect->height == image->buffer->height && rect->x == 0 && rect->y == 0) {
|
||||
|
||||
memcpy (image->buffer->data->Data (), bytes->Data (), bytes->Length ());
|
||||
return;
|
||||
|
||||
}
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
PixelFormat sourceFormat = image->buffer->format;
|
||||
bool premultiplied = image->buffer->premultiplied;
|
||||
ImageDataView dataView = ImageDataView (image, rect);
|
||||
int row;
|
||||
RGBA pixel;
|
||||
|
||||
int offset = int (image->buffer->width * (rect->y + image->offsetX) + (rect->x + image->offsetY));
|
||||
int boundR = int ((rect->x + rect->width + image->offsetX));
|
||||
int width = image->buffer->width;
|
||||
int color;
|
||||
uint8_t* byteArray = (uint8_t*)bytes->Data ();
|
||||
int srcPosition = 0;
|
||||
|
||||
if (format == ARGB) {
|
||||
bool transparent = image->buffer->transparent;
|
||||
|
||||
for (int y = 0; y < dataView.height; y++) {
|
||||
|
||||
int pos = offset * 4;
|
||||
int len = int (rect->width * rect->height * 4);
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
uint8_t* byteArray = (uint8_t*)bytes->Data ();
|
||||
row = dataView.Row (y);
|
||||
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
for (int x = 0; x < dataView.width; x++) {
|
||||
|
||||
pixel.ReadUInt8 (byteArray, srcPosition, format, false);
|
||||
if (!transparent) pixel.a = 0xFF;
|
||||
pixel.WriteUInt8 (data, row + (x * 4), sourceFormat, premultiplied);
|
||||
|
||||
if (((pos) % (width * 4)) >= boundR * 4) {
|
||||
|
||||
pos += (width - boundR) * 4;
|
||||
|
||||
}
|
||||
|
||||
data[pos] = byteArray[i + 1];
|
||||
data[pos + 1] = byteArray[i + 2];
|
||||
data[pos + 2] = byteArray[i + 3];
|
||||
data[pos + 3] = byteArray[i];
|
||||
pos += 4;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int pos = offset;
|
||||
int len = int (rect->width * rect->height);
|
||||
int* data = (int*)image->buffer->data->Data ();
|
||||
int* byteArray = (int*)bytes->Data ();
|
||||
|
||||
// TODO: memcpy rows at once
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
||||
if (((pos) % (width)) >= boundR) {
|
||||
|
||||
pos += (width - boundR);
|
||||
|
||||
}
|
||||
|
||||
data[pos] = byteArray[i];
|
||||
pos++;
|
||||
srcPosition += 4;
|
||||
|
||||
}
|
||||
|
||||
@@ -629,28 +608,64 @@ namespace lime {
|
||||
|
||||
void ImageDataUtil::UnmultiplyAlpha (Image* image) {
|
||||
|
||||
int length = image->buffer->data->Length () / 4;
|
||||
PixelFormat format = image->buffer->format;
|
||||
uint8_t* data = (uint8_t*)image->buffer->data->Data ();
|
||||
|
||||
int unmultiply;
|
||||
uint8_t a;
|
||||
int length = int (image->buffer->data->Length () / 4);
|
||||
RGBA pixel;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
a = data[3];
|
||||
|
||||
if (a != 0) {
|
||||
|
||||
unmultiply = 255.0 / a;
|
||||
data[0] = __clamp[data[0] * unmultiply];
|
||||
data[1] = __clamp[data[1] * unmultiply];
|
||||
data[2] = __clamp[data[2] * unmultiply];
|
||||
|
||||
}
|
||||
pixel.ReadUInt8 (data, i * 4, format, true);
|
||||
pixel.WriteUInt8 (data, i * 4, format, false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
ImageDataView::ImageDataView (Image* image, Rectangle* rect) {
|
||||
|
||||
this->image = image;
|
||||
|
||||
if (rect->x < 0) rect->x = 0;
|
||||
if (rect->y < 0) rect->y = 0;
|
||||
if (rect->x + rect->width > image->width) rect->width = image->width - rect->x;
|
||||
if (rect->y + rect->height > image->height) rect->height = image->height - rect->y;
|
||||
if (rect->width < 0) rect->width = 0;
|
||||
if (rect->height < 0) rect->height = 0;
|
||||
this->rect = rect;
|
||||
|
||||
stride = image->buffer->Stride ();
|
||||
|
||||
x = ceil (this->rect->x);
|
||||
y = ceil (this->rect->y);
|
||||
width = floor (this->rect->width);
|
||||
height = floor (this->rect->height);
|
||||
offset = (stride * (this->y + image->offsetY)) + ((this->x + image->offsetX) * 4);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ImageDataView::Clip (int x, int y, int width, int height) {
|
||||
|
||||
rect->Contract (x, y, width, height);
|
||||
|
||||
this->x = ceil (rect->x);
|
||||
this->y = ceil (rect->y);
|
||||
this->width = floor (rect->width);
|
||||
this->height = floor (rect->height);
|
||||
offset = (stride * (this->y + image->offsetY)) + ((this->x + image->offsetX) * 4);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline int ImageDataView::Row (int y) {
|
||||
|
||||
return offset + stride * y;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -57,67 +57,111 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetAlphaMultiplier () {
|
||||
float inline ColorMatrix::GetAlphaMultiplier () {
|
||||
|
||||
return data[18];
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetAlphaOffset () {
|
||||
float inline ColorMatrix::GetAlphaOffset () {
|
||||
|
||||
return data[19] * 255;
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetBlueMultiplier () {
|
||||
void ColorMatrix::GetAlphaTable (unsigned char* table) {
|
||||
|
||||
GetDataTable (table, GetAlphaMultiplier (), GetAlphaOffset ());
|
||||
|
||||
}
|
||||
|
||||
|
||||
float inline ColorMatrix::GetBlueMultiplier () {
|
||||
|
||||
return data[12];
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetBlueOffset () {
|
||||
float inline ColorMatrix::GetBlueOffset () {
|
||||
|
||||
return data[14] * 255;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int ColorMatrix::GetColor () {
|
||||
void ColorMatrix::GetBlueTable (unsigned char* table) {
|
||||
|
||||
GetDataTable (table, GetBlueMultiplier (), GetBlueOffset ());
|
||||
|
||||
}
|
||||
|
||||
|
||||
int32_t inline ColorMatrix::GetColor () {
|
||||
|
||||
return ((int (GetRedOffset ()) << 16) | (int (GetGreenOffset ()) << 8) | int (GetBlueOffset ()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetGreenMultiplier () {
|
||||
void inline ColorMatrix::GetDataTable (unsigned char* table, float multiplier, float offset) {
|
||||
|
||||
int32_t value;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
value = (int)(i * multiplier + offset);
|
||||
if (value > 0xFF) value = 0xFF;
|
||||
if (value < 0) value = 0;
|
||||
table[i] = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
float inline ColorMatrix::GetGreenMultiplier () {
|
||||
|
||||
return data[6];
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetGreenOffset () {
|
||||
float inline ColorMatrix::GetGreenOffset () {
|
||||
|
||||
return data[9] * 255;
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetRedMultiplier () {
|
||||
void ColorMatrix::GetGreenTable (unsigned char* table) {
|
||||
|
||||
GetDataTable (table, GetGreenMultiplier (), GetGreenOffset ());
|
||||
|
||||
}
|
||||
|
||||
|
||||
float inline ColorMatrix::GetRedMultiplier () {
|
||||
|
||||
return data[0];
|
||||
|
||||
}
|
||||
|
||||
|
||||
float ColorMatrix::GetRedOffset () {
|
||||
float inline ColorMatrix::GetRedOffset () {
|
||||
|
||||
return data[4] * 255;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ColorMatrix::GetRedTable (unsigned char* table) {
|
||||
|
||||
GetDataTable (table, GetRedMultiplier (), GetRedOffset ());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -51,4 +51,23 @@ namespace lime {
|
||||
}
|
||||
|
||||
|
||||
void Rectangle::Contract (double x, double y, double width, double height) {
|
||||
|
||||
if (this->width == 0 && this->height == 0) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
//double cacheRight = this->x + this->width;
|
||||
//double cacheBottom = this->y + this->height;
|
||||
|
||||
if (this->x < x) this->x = x;
|
||||
if (this->y < y) this->y = y;
|
||||
if (this->x + this->width > x + width) this->width = x + width - this->x;
|
||||
if (this->y + this->height > y + height) this->height = y + height - this->y;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user