From 45054e39d657fe6249bfbbaf2f7e065ac52e9e71 Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Tue, 12 Sep 2017 12:32:02 -0700 Subject: [PATCH] Improve handling of Image endianness (resolve #1018, resolve #1070) --- lime/_backend/native/NativeCFFI.hx | 2 +- lime/graphics/Image.hx | 10 +++-- lime/graphics/utils/ImageCanvasUtil.hx | 5 ++- lime/graphics/utils/ImageDataUtil.hx | 18 ++++++-- .../include/graphics/utils/ImageDataUtil.h | 3 +- project/include/math/color/RGBA.h | 18 ++++++-- project/include/system/Endian.h | 19 ++++++++ project/src/ExternalInterface.cpp | 8 ++-- project/src/graphics/utils/ImageDataUtil.cpp | 43 +++++++++---------- 9 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 project/include/system/Endian.h diff --git a/lime/_backend/native/NativeCFFI.hx b/lime/_backend/native/NativeCFFI.hx index 0f17ee78a..caf9dac87 100644 --- a/lime/_backend/native/NativeCFFI.hx +++ b/lime/_backend/native/NativeCFFI.hx @@ -89,7 +89,7 @@ class NativeCFFI { @:cffi private static function lime_image_data_util_multiply_alpha (image:Dynamic):Void; @:cffi private static function lime_image_data_util_resize (image:Dynamic, buffer:Dynamic, width:Int, height:Int):Void; @:cffi private static function lime_image_data_util_set_format (image:Dynamic, format:Int):Void; - @:cffi private static function lime_image_data_util_set_pixels (image:Dynamic, rect:Dynamic, bytes:Dynamic, offset:Int, format:Int):Void; + @:cffi private static function lime_image_data_util_set_pixels (image:Dynamic, rect:Dynamic, bytes:Dynamic, offset:Int, format:Int, endian:Int):Void; @:cffi private static function lime_image_data_util_threshold (image:Dynamic, sourceImage:Dynamic, sourceRect:Dynamic, destPoint:Dynamic, operation:Int, thresholdRG:Int, thresholdBA:Int, colorRG:Int, colorBA:Int, maskRG:Int, maskBA:Int, copySource:Bool):Int; @:cffi private static function lime_image_data_util_unmultiply_alpha (image:Dynamic):Void; @:cffi private static function lime_joystick_get_device_guid (id:Int):Dynamic; diff --git a/lime/graphics/Image.hx b/lime/graphics/Image.hx index 8902f1095..b60938a26 100644 --- a/lime/graphics/Image.hx +++ b/lime/graphics/Image.hx @@ -23,6 +23,8 @@ import lime.math.Rectangle; import lime.math.Vector2; import lime.net.HTTPRequest; import lime.system.CFFI; +import lime.system.Endian; +import lime.system.System; import lime.utils.ArrayBuffer; import lime.utils.BytePointer; import lime.utils.Log; @@ -1074,16 +1076,18 @@ class Image { } - public function setPixels (rect:Rectangle, bytePointer:BytePointer, format:PixelFormat = null):Void { + public function setPixels (rect:Rectangle, bytePointer:BytePointer, format:PixelFormat = null, endian:Endian = null):Void { rect = __clipRect (rect); if (buffer == null || rect == null) return; + //if (endian == null) endian = System.endianness; // TODO: System endian order + if (endian == null) endian = BIG_ENDIAN; switch (type) { case CANVAS: - ImageCanvasUtil.setPixels (this, rect, bytePointer, format); + ImageCanvasUtil.setPixels (this, rect, bytePointer, format, endian); case DATA: @@ -1091,7 +1095,7 @@ class Image { ImageCanvasUtil.convertToData (this); #end - ImageDataUtil.setPixels (this, rect, bytePointer, format); + ImageDataUtil.setPixels (this, rect, bytePointer, format, endian); case FLASH: diff --git a/lime/graphics/utils/ImageCanvasUtil.hx b/lime/graphics/utils/ImageCanvasUtil.hx index 144bdf7ab..3e6f61e95 100644 --- a/lime/graphics/utils/ImageCanvasUtil.hx +++ b/lime/graphics/utils/ImageCanvasUtil.hx @@ -9,6 +9,7 @@ import lime.graphics.PixelFormat; import lime.math.ColorMatrix; import lime.math.Rectangle; import lime.math.Vector2; +import lime.system.Endian; import lime.utils.BytePointer; import lime.utils.UInt8Array; @@ -411,11 +412,11 @@ class ImageCanvasUtil { } - public static function setPixels (image:Image, rect:Rectangle, bytePointer:BytePointer, format:PixelFormat):Void { + public static function setPixels (image:Image, rect:Rectangle, bytePointer:BytePointer, format:PixelFormat, endian:Endian):Void { convertToData (image); - ImageDataUtil.setPixels (image, rect, bytePointer, format); + ImageDataUtil.setPixels (image, rect, bytePointer, format, endian); } diff --git a/lime/graphics/utils/ImageDataUtil.hx b/lime/graphics/utils/ImageDataUtil.hx index 8e3eb8f44..bfb3494da 100644 --- a/lime/graphics/utils/ImageDataUtil.hx +++ b/lime/graphics/utils/ImageDataUtil.hx @@ -15,6 +15,7 @@ import lime.math.ColorMatrix; import lime.math.Rectangle; import lime.math.Vector2; import lime.system.CFFI; +import lime.system.Endian; import lime.utils.BytePointer; import lime.utils.UInt8Array; @@ -1261,12 +1262,12 @@ class ImageDataUtil { } - public static function setPixels (image:Image, rect:Rectangle, bytePointer:BytePointer, format:PixelFormat):Void { + public static function setPixels (image:Image, rect:Rectangle, bytePointer:BytePointer, format:PixelFormat, endian:Endian):Void { if (image.buffer.data == null) return; #if (lime_cffi && !disable_cffi && !macro) - if (CFFI.enabled) NativeCFFI.lime_image_data_util_set_pixels (image, rect, bytePointer.bytes, bytePointer.offset, format); else + if (CFFI.enabled) NativeCFFI.lime_image_data_util_set_pixels (image, rect, bytePointer.bytes, bytePointer.offset, format, endian == BIG_ENDIAN ? 1 : 0); else #end { @@ -1278,6 +1279,7 @@ class ImageDataUtil { var transparent = image.transparent; var bytes = bytePointer.bytes; var dataPosition = bytePointer.offset; + var littleEndian = (endian != BIG_ENDIAN); for (y in 0...dataView.height) { @@ -1285,8 +1287,16 @@ class ImageDataUtil { for (x in 0...dataView.width) { - //color = bytes.getInt32 (dataPosition); - color = bytes.get (dataPosition + 3) | (bytes.get (dataPosition + 2) << 8) | (bytes.get (dataPosition + 1) << 16) | (bytes.get (dataPosition) << 24); + if (littleEndian) { + + color = bytes.getInt32 (dataPosition); // can this be trusted on big endian systems? + + } else { + + color = bytes.get (dataPosition + 3) | (bytes.get (dataPosition + 2) << 8) | (bytes.get (dataPosition + 1) << 16) | (bytes.get (dataPosition) << 24); + + } + dataPosition += 4; switch (format) { diff --git a/project/include/graphics/utils/ImageDataUtil.h b/project/include/graphics/utils/ImageDataUtil.h index b41ce9823..a9c7390fa 100644 --- a/project/include/graphics/utils/ImageDataUtil.h +++ b/project/include/graphics/utils/ImageDataUtil.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ namespace lime { static void MultiplyAlpha (Image* image); static void Resize (Image* image, ImageBuffer* buffer, int width, int height); static void SetFormat (Image* image, PixelFormat format); - static void SetPixels (Image* image, Rectangle* rect, Bytes* bytes, int offset, PixelFormat format); + static void SetPixels (Image* image, Rectangle* rect, Bytes* bytes, int offset, PixelFormat format, Endian endian); static int Threshold (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int operation, int32_t threshold, int32_t color, int32_t mask, bool copySource); static void UnmultiplyAlpha (Image* image); diff --git a/project/include/math/color/RGBA.h b/project/include/math/color/RGBA.h index b39514d64..3c3fb46fa 100644 --- a/project/include/math/color/RGBA.h +++ b/project/include/math/color/RGBA.h @@ -3,6 +3,7 @@ #include +#include #include #include @@ -110,23 +111,32 @@ namespace lime { } - inline void ReadUInt8 (const unsigned char* data, int offset, PixelFormat format, bool premultiplied) { + inline void ReadUInt8 (const unsigned char* data, int offset, PixelFormat format, bool premultiplied, Endian endian) { switch (format) { case BGRA32: - Set (data[offset + 2], data[offset + 1], data[offset], data[offset + 3]); + if (endian == LITTLE_ENDIAN) + Set (data[offset + 1], data[offset + 2], data[offset + 3], data[offset]); + else + 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]); + if (endian == LITTLE_ENDIAN) + Set (data[offset + 3], data[offset + 2], data[offset + 1], data[offset]); + else + 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]); + if (endian == LITTLE_ENDIAN) + Set (data[offset + 2], data[offset + 1], data[offset], data[offset + 3]); + else + Set (data[offset + 1], data[offset + 2], data[offset + 3], data[offset]); break; } diff --git a/project/include/system/Endian.h b/project/include/system/Endian.h new file mode 100644 index 000000000..48d2b5f79 --- /dev/null +++ b/project/include/system/Endian.h @@ -0,0 +1,19 @@ +#ifndef LIME_SYSTEM_ENDIAN_H +#define LIME_SYSTEM_ENDIAN_H + + +namespace lime { + + + enum Endian { + + LITTLE_ENDIAN, + BIG_ENDIAN + + }; + + +} + + +#endif \ No newline at end of file diff --git a/project/src/ExternalInterface.cpp b/project/src/ExternalInterface.cpp index 255a5c232..5627543bd 100644 --- a/project/src/ExternalInterface.cpp +++ b/project/src/ExternalInterface.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -978,13 +979,14 @@ namespace lime { } - void lime_image_data_util_set_pixels (value image, value rect, value bytes, int offset, int format) { + void lime_image_data_util_set_pixels (value image, value rect, value bytes, int offset, int format, int endian) { Image _image = Image (image); Rectangle _rect = Rectangle (rect); Bytes _bytes (bytes); PixelFormat _format = (PixelFormat)format; - ImageDataUtil::SetPixels (&_image, &_rect, &_bytes, offset, _format); + Endian _endian = (Endian)endian; + ImageDataUtil::SetPixels (&_image, &_rect, &_bytes, offset, _format, _endian); } @@ -1847,7 +1849,7 @@ namespace lime { DEFINE_PRIME1v (lime_image_data_util_multiply_alpha); DEFINE_PRIME4v (lime_image_data_util_resize); DEFINE_PRIME2v (lime_image_data_util_set_format); - DEFINE_PRIME5v (lime_image_data_util_set_pixels); + DEFINE_PRIME6v (lime_image_data_util_set_pixels); DEFINE_PRIME12 (lime_image_data_util_threshold); DEFINE_PRIME1v (lime_image_data_util_unmultiply_alpha); DEFINE_PRIME4 (lime_image_encode); diff --git a/project/src/graphics/utils/ImageDataUtil.cpp b/project/src/graphics/utils/ImageDataUtil.cpp index 4a18bcb36..fe3aad6f3 100644 --- a/project/src/graphics/utils/ImageDataUtil.cpp +++ b/project/src/graphics/utils/ImageDataUtil.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -38,7 +37,7 @@ namespace lime { offset = row + (x * 4); - pixel.ReadUInt8 (data, offset, format, premultiplied); + pixel.ReadUInt8 (data, offset, format, premultiplied, BIG_ENDIAN); pixel.Set (redTable[pixel.r], greenTable[pixel.g], blueTable[pixel.b], alphaTable[pixel.a]); pixel.WriteUInt8 (data, offset, format, premultiplied); @@ -74,8 +73,8 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - srcPixel.ReadUInt8 (srcData, srcPosition, srcFormat, srcPremultiplied); - destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied); + srcPixel.ReadUInt8 (srcData, srcPosition, srcFormat, srcPremultiplied, BIG_ENDIAN); + destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied, BIG_ENDIAN); switch (srcChannel) { @@ -144,8 +143,8 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); - destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied); + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied, BIG_ENDIAN); + destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied, BIG_ENDIAN); sourceAlpha = sourcePixel.a / 255.0; destAlpha = destPixel.a / 255.0; @@ -194,7 +193,7 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied, BIG_ENDIAN); sourcePixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied); sourcePosition += 4; @@ -228,9 +227,9 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); - destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied); - alphaPixel.ReadUInt8 (alphaData, alphaPosition, alphaFormat, false); + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied, BIG_ENDIAN); + destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied, BIG_ENDIAN); + alphaPixel.ReadUInt8 (alphaData, alphaPosition, alphaFormat, false, BIG_ENDIAN); sourceAlpha = (alphaPixel.a / 255.0) * (sourcePixel.a / 255.0); @@ -267,8 +266,8 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); - alphaPixel.ReadUInt8 (alphaData, alphaPosition, alphaFormat, false); + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied, BIG_ENDIAN); + alphaPixel.ReadUInt8 (alphaData, alphaPosition, alphaFormat, false, BIG_ENDIAN); sourcePixel.a = int (0.5 + (sourcePixel.a * (alphaPixel.a / 255.0))); sourcePixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied); @@ -338,7 +337,7 @@ namespace lime { if (premultiplied) fillColor.MultiplyAlpha (); RGBA hitColor; - hitColor.ReadUInt8 (data, ((y + image->offsetY) * (image->buffer->width * 4)) + ((x + image->offsetX) * 4), format, premultiplied); + hitColor.ReadUInt8 (data, ((y + image->offsetY) * (image->buffer->width * 4)) + ((x + image->offsetX) * 4), format, premultiplied, BIG_ENDIAN); if (!image->buffer->transparent) { @@ -381,7 +380,7 @@ namespace lime { } nextPointOffset = (nextPointY * image->width + nextPointX) * 4; - readColor.ReadUInt8 (data, nextPointOffset, format, premultiplied); + readColor.ReadUInt8 (data, nextPointOffset, format, premultiplied, BIG_ENDIAN); if (readColor == hitColor) { @@ -420,7 +419,7 @@ namespace lime { for (int x = 0; x < dataView.width; x++) { - pixel.ReadUInt8 (data, position, sourceFormat, premultiplied); + pixel.ReadUInt8 (data, position, sourceFormat, premultiplied, BIG_ENDIAN); pixel.WriteUInt8 (destData, destPosition, format, false); position += 4; @@ -456,8 +455,8 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); - destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied); + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied, BIG_ENDIAN); + destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied, BIG_ENDIAN); destPixel.r = int (((sourcePixel.r * redMultiplier) + (destPixel.r * (256 - redMultiplier))) / 256); destPixel.g = int (((sourcePixel.g * greenMultiplier) + (destPixel.g * (256 - greenMultiplier))) / 256); @@ -485,7 +484,7 @@ namespace lime { for (int i = 0; i < length; i++) { - pixel.ReadUInt8 (data, i * 4, format, false); + pixel.ReadUInt8 (data, i * 4, format, false, BIG_ENDIAN); pixel.WriteUInt8 (data, i * 4, format, true); } @@ -634,7 +633,7 @@ namespace lime { } - void ImageDataUtil::SetPixels (Image* image, Rectangle* rect, Bytes* bytes, int offset, PixelFormat format) { + void ImageDataUtil::SetPixels (Image* image, Rectangle* rect, Bytes* bytes, int offset, PixelFormat format, Endian endian) { uint8_t* data = (uint8_t*)image->buffer->data->Data (); PixelFormat sourceFormat = image->buffer->format; @@ -654,7 +653,7 @@ namespace lime { for (int x = 0; x < dataView.width; x++) { - pixel.ReadUInt8 (byteArray, srcPosition, format, false); + pixel.ReadUInt8 (byteArray, srcPosition, format, false, endian); if (!transparent) pixel.a = 0xFF; pixel.WriteUInt8 (data, row + (x * 4), sourceFormat, premultiplied); @@ -752,7 +751,7 @@ namespace lime { for (int x = 0; x < destView.width; x++) { - srcPixel.ReadUInt8 (srcData, srcPosition, srcFormat, srcPremultiplied); + srcPixel.ReadUInt8 (srcData, srcPosition, srcFormat, srcPremultiplied, BIG_ENDIAN); pixelMask = srcPixel.Get () & mask; @@ -801,7 +800,7 @@ namespace lime { for (int i = 0; i < length; i++) { - pixel.ReadUInt8 (data, i * 4, format, true); + pixel.ReadUInt8 (data, i * 4, format, true, BIG_ENDIAN); pixel.WriteUInt8 (data, i * 4, format, false); }