@amazon-devices/expo-image 2.0.9000000000 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +80 -58
- package/README.kepler.md +1 -0
- package/build/ExpoImageModules.kepler.d.ts.map +1 -1
- package/build/ExpoImageModules.kepler.js +1 -1
- package/build/ExpoImageModules.kepler.js.map +1 -1
- package/build/ImageViewNativeComponent.js +1 -1
- package/build/ImageViewNativeComponent.js.map +1 -1
- package/kepler/AutoLinkInit.cpp +17 -0
- package/kepler/Constants.h +21 -0
- package/kepler/ExpoImage/ExpoImageTurboModule.cpp +148 -0
- package/kepler/ExpoImage/ExpoImageTurboModule.h +41 -0
- package/kepler/ExpoImage/PrefetchListener.cpp +45 -0
- package/kepler/ExpoImage/PrefetchListener.h +40 -0
- package/kepler/ExpoImageComponent/AccessibilityDelegate.cpp +33 -0
- package/kepler/ExpoImageComponent/AccessibilityDelegate.h +30 -0
- package/kepler/ExpoImageComponent/ExpoImageComponentViewManager.cpp +132 -0
- package/kepler/ExpoImageComponent/ExpoImageComponentViewManager.h +66 -0
- package/kepler/ExpoImageComponent/ExpoImageStore.h +102 -0
- package/kepler/ExpoImageComponent/ExpoImageView.cpp +556 -0
- package/kepler/ExpoImageComponent/ExpoImageView.h +186 -0
- package/kepler/ExpoImageComponent/HashImageDecoder.cpp +91 -0
- package/kepler/ExpoImageComponent/HashImageDecoder.h +18 -0
- package/kepler/ExpoImageComponent/ImageLoadListeners.cpp +73 -0
- package/kepler/ExpoImageComponent/ImageLoadListeners.h +52 -0
- package/kepler/ExpoImageComponent/TransitionAnimator.cpp +113 -0
- package/kepler/ExpoImageComponent/TransitionAnimator.h +32 -0
- package/kepler/ImageSourceInfo.h +21 -0
- package/kepler/ViewManagerFactory.cpp +36 -0
- package/kepler/ViewManagerFactory.h +29 -0
- package/kepler/blurhash/common.h +11 -10
- package/kepler/blurhash/decode.cpp +26 -16
- package/kepler/contentposition/ImageContentPosition.cpp +50 -69
- package/kepler/contentposition/ImageContentPosition.h +36 -26
- package/kepler/enums/ImageContentFit.h +47 -15
- package/kepler/enums/ImagePriority.h +12 -13
- package/kepler/test/blurhash/decode.cpp +36 -27
- package/kepler/test/contentposition/ImageContentPosition.cpp +70 -76
- package/kepler/test/thumbhash/decode.cpp +18 -10
- package/kepler/test/utils/Base64.cpp +73 -0
- package/kepler/test/utils/ColorUtils.cpp +53 -0
- package/kepler/test/utils/SourceSelection.cpp +87 -0
- package/kepler/thumbhash/decode.cpp +7 -16
- package/kepler/utils/Base64.cpp +85 -0
- package/kepler/utils/Base64.h +36 -0
- package/kepler/utils/ColorUtils.h +26 -0
- package/kepler/utils/SourceSelection.cpp +80 -0
- package/kepler/utils/SourceSelection.h +65 -0
- package/lib/aarch64-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
- package/lib/aarch64-release/libexpo-image-3.so +0 -0
- package/lib/armv7-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
- package/lib/armv7-release/libexpo-image-3.so +0 -0
- package/lib/x86_64-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
- package/lib/x86_64-release/libexpo-image-3.so +0 -0
- package/package.json +1 -1
- package/react-native.config.js +12 -4
- package/src/ExpoImageModules.kepler.ts +1 -1
- package/src/ImageViewNativeComponent.ts +1 -1
- package/kepler/ExpoImage.cpp +0 -63
- package/kepler/ExpoImage.h +0 -29
- package/kepler/ExpoImageCacheHandler.cpp +0 -15
- package/kepler/ExpoImageCacheHandler.h +0 -28
- package/kepler/ExpoImageComponent.cpp +0 -579
- package/kepler/ExpoImageComponent.h +0 -199
- package/kepler/ImageWrapper/BlurhashImageWrapper.cpp +0 -37
- package/kepler/ImageWrapper/BlurhashImageWrapper.h +0 -23
- package/kepler/ImageWrapper/CacheImageWrapper.cpp +0 -80
- package/kepler/ImageWrapper/CacheImageWrapper.h +0 -41
- package/kepler/ImageWrapper/HashImageWrapper.cpp +0 -21
- package/kepler/ImageWrapper/HashImageWrapper.h +0 -22
- package/kepler/ImageWrapper/ImageWrapper.cpp +0 -24
- package/kepler/ImageWrapper/ImageWrapper.h +0 -33
- package/kepler/ImageWrapper/LocalImageWrapper.cpp +0 -61
- package/kepler/ImageWrapper/LocalImageWrapper.h +0 -30
- package/kepler/ImageWrapper/ThumbhashImageWrapper.cpp +0 -33
- package/kepler/ImageWrapper/ThumbhashImageWrapper.h +0 -20
- package/kepler/KeplerImageViewComponent.h +0 -64
- package/kepler/KeplerInit.cpp +0 -35
- package/kepler/managers/ExpoImageManager.cpp +0 -38
- package/kepler/managers/ExpoImageManager.h +0 -23
- package/kepler/test/ExpoImage.cpp +0 -73
- package/kepler/test/ExpoImageComponent.cpp +0 -304
- package/kepler/test/managers/ExpoImageManager.cpp +0 -39
- package/kepler/test/utils/LoaderDelegate.cpp +0 -130
- package/kepler/utils/PlaceholderImageLoaderDelegate.cpp +0 -26
- package/kepler/utils/PlaceholderImageLoaderDelegate.h +0 -26
- package/kepler/utils/PrefetchImageLoaderDelegate.cpp +0 -55
- package/kepler/utils/PrefetchImageLoaderDelegate.h +0 -35
- package/kepler/utils/SourceImageLoaderDelegate.cpp +0 -47
- package/kepler/utils/SourceImageLoaderDelegate.h +0 -42
- package/kepler/utils/SourceImageLoaderDelegateEmitter.cpp +0 -15
- package/kepler/utils/SourceImageLoaderDelegateEmitter.h +0 -28
- package/kepler-compatibility.json +0 -6
|
@@ -1,100 +1,94 @@
|
|
|
1
1
|
#include "../../contentposition/ImageContentPosition.h"
|
|
2
2
|
|
|
3
|
-
#include <gmock/gmock-actions.h>
|
|
4
|
-
#include <gmock/gmock.h>
|
|
5
3
|
#include <gtest/gtest.h>
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
namespace expoimage {
|
|
6
|
+
namespace {
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
// Helpers to make test intent clearer.
|
|
9
|
+
ImageContentPositionValue px(float v) { return ImageContentPositionValue{v, false}; }
|
|
10
|
+
ImageContentPositionValue empty() { return ImageContentPositionValue{}; }
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
volta::graphics::Size viewRect{50.0f, 50.0f};
|
|
12
|
+
// ---- ImageContentPositionValue ----
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
TEST(ImageContentPositionValue, EmptyReturnsNullopt) {
|
|
15
|
+
ImageContentPositionValue value;
|
|
16
|
+
Size imageSize{100.0f, 100.0f};
|
|
17
|
+
Size viewSize{50.0f, 50.0f};
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const volta::graphics::Size& viewRect,
|
|
21
|
-
bool isPercentage,
|
|
22
|
-
bool isReverse) -> float { return 2137.0f; };
|
|
19
|
+
auto calc = [](float, const Size&, const Size&, bool, bool) { return 123.0f; };
|
|
20
|
+
auto result = value.calcOffset(false, imageSize, viewSize, calc);
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
EXPECT_FALSE(emptyResult.has_value());
|
|
27
|
-
|
|
28
|
-
auto result = value.calcOffset(false, imageRect, viewRect, calc);
|
|
29
|
-
EXPECT_EQ(result.value(), 2137.0f);
|
|
30
|
-
}
|
|
22
|
+
EXPECT_FALSE(result.has_value());
|
|
31
23
|
}
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
ImageContentPositionValue
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
ImageContentPositionValue right{10.0f, false};
|
|
38
|
-
volta::graphics::Size imageRect{100.0f, 100.0f};
|
|
39
|
-
volta::graphics::Size viewRect{50.0f, 50.0f};
|
|
40
|
-
|
|
41
|
-
SECTION("offsetX with left position") {
|
|
42
|
-
ImageContentPosition pos;
|
|
43
|
-
|
|
44
|
-
float offset;
|
|
45
|
-
|
|
46
|
-
pos = ImageContentPosition(top, bottom, {}, {10.f, false});
|
|
47
|
-
offset = pos.offsetX(imageRect, viewRect);
|
|
48
|
-
EXPECT_EQ(offset, 10.f);
|
|
49
|
-
}
|
|
25
|
+
TEST(ImageContentPositionValue, SetValueInvokesCalc) {
|
|
26
|
+
ImageContentPositionValue value{10.0f, false};
|
|
27
|
+
Size imageSize{100.0f, 100.0f};
|
|
28
|
+
Size viewSize{50.0f, 50.0f};
|
|
50
29
|
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
auto calc = [](float, const Size&, const Size&, bool, bool) { return 2137.0f; };
|
|
31
|
+
auto result = value.calcOffset(false, imageSize, viewSize, calc);
|
|
53
32
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
offset = pos.offsetX(imageRect, viewRect);
|
|
58
|
-
EXPECT_EQ(offset, 20.f);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
SECTION("offsetX without left and right positions") {
|
|
62
|
-
ImageContentPosition pos;
|
|
63
|
-
|
|
64
|
-
float offset;
|
|
33
|
+
ASSERT_TRUE(result.has_value());
|
|
34
|
+
EXPECT_FLOAT_EQ(result.value(), 2137.0f);
|
|
35
|
+
}
|
|
65
36
|
|
|
66
|
-
|
|
67
|
-
offset = pos.offsetX(imageRect, viewRect);
|
|
68
|
-
EXPECT_EQ(offset, -25.f);
|
|
69
|
-
}
|
|
37
|
+
// ---- ImageContentPosition ----
|
|
70
38
|
|
|
71
|
-
|
|
72
|
-
|
|
39
|
+
class ImageContentPositionTest : public ::testing::Test {
|
|
40
|
+
protected:
|
|
41
|
+
Size imageRect_{100.0f, 100.0f};
|
|
42
|
+
Size viewRect_{50.0f, 50.0f};
|
|
43
|
+
};
|
|
73
44
|
|
|
74
|
-
|
|
45
|
+
TEST_F(ImageContentPositionTest, OffsetX_LeftTakesPrecedence) {
|
|
46
|
+
ImageContentPosition pos(empty(), empty(), empty(), px(10.0f));
|
|
47
|
+
EXPECT_FLOAT_EQ(pos.offsetX(imageRect_, viewRect_), 10.0f);
|
|
48
|
+
}
|
|
75
49
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
50
|
+
TEST_F(ImageContentPositionTest, OffsetX_RightWhenLeftEmpty) {
|
|
51
|
+
// right=20px means image right edge is inset 20px from view right edge.
|
|
52
|
+
// Reverse translation: viewWidth - imageWidth - 20 = 50 - 100 - 20 = -70
|
|
53
|
+
ImageContentPosition pos(empty(), empty(), px(20.0f), empty());
|
|
54
|
+
EXPECT_FLOAT_EQ(pos.offsetX(imageRect_, viewRect_), -70.0f);
|
|
55
|
+
}
|
|
80
56
|
|
|
81
|
-
|
|
82
|
-
|
|
57
|
+
TEST_F(ImageContentPositionTest, OffsetX_DefaultCenters) {
|
|
58
|
+
// No left or right → default is center (reverse of 50%): (50-100)/2 = -25
|
|
59
|
+
ImageContentPosition pos(empty(), empty(), empty(), empty());
|
|
60
|
+
EXPECT_FLOAT_EQ(pos.offsetX(imageRect_, viewRect_), -25.0f);
|
|
61
|
+
}
|
|
83
62
|
|
|
84
|
-
|
|
63
|
+
TEST_F(ImageContentPositionTest, OffsetY_TopTakesPrecedence) {
|
|
64
|
+
ImageContentPosition pos(px(10.0f), empty(), empty(), empty());
|
|
65
|
+
EXPECT_FLOAT_EQ(pos.offsetY(imageRect_, viewRect_), 10.0f);
|
|
66
|
+
}
|
|
85
67
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
68
|
+
TEST_F(ImageContentPositionTest, OffsetY_BottomWhenTopEmpty) {
|
|
69
|
+
// bottom=10 → reverse: viewH - imageH - 10 = 50 - 100 - 10 = -60
|
|
70
|
+
ImageContentPosition pos(empty(), px(10.0f), empty(), empty());
|
|
71
|
+
EXPECT_FLOAT_EQ(pos.offsetY(imageRect_, viewRect_), -60.0f);
|
|
72
|
+
}
|
|
90
73
|
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
TEST_F(ImageContentPositionTest, OffsetY_DefaultCenters) {
|
|
75
|
+
ImageContentPosition pos(empty(), empty(), empty(), empty());
|
|
76
|
+
EXPECT_FLOAT_EQ(pos.offsetY(imageRect_, viewRect_), -25.0f);
|
|
77
|
+
}
|
|
93
78
|
|
|
94
|
-
|
|
79
|
+
TEST_F(ImageContentPositionTest, Percentage_LeftAtZero) {
|
|
80
|
+
// left=0% → 0
|
|
81
|
+
ImageContentPositionValue leftPct{0.0f, true};
|
|
82
|
+
ImageContentPosition pos(empty(), empty(), empty(), leftPct);
|
|
83
|
+
EXPECT_FLOAT_EQ(pos.offsetX(imageRect_, viewRect_), 0.0f);
|
|
84
|
+
}
|
|
95
85
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
TEST_F(ImageContentPositionTest, Percentage_LeftAt100) {
|
|
87
|
+
// left=100% → viewW - imageW = 50 - 100 = -50
|
|
88
|
+
ImageContentPositionValue leftPct{100.0f, true};
|
|
89
|
+
ImageContentPosition pos(empty(), empty(), empty(), leftPct);
|
|
90
|
+
EXPECT_FLOAT_EQ(pos.offsetX(imageRect_, viewRect_), -50.0f);
|
|
100
91
|
}
|
|
92
|
+
|
|
93
|
+
} // namespace
|
|
94
|
+
} // namespace expoimage
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
* Use is subject to license terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
#include "../../thumbhash/decode.h"
|
|
2
9
|
|
|
3
|
-
#include <gmock/gmock-actions.h>
|
|
4
|
-
#include <gmock/gmock.h>
|
|
5
10
|
#include <gtest/gtest.h>
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
namespace expoimage {
|
|
13
|
+
namespace {
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
std::string hash = "1QcSHQRnh493V4dIh4eXh1h4kJUI";
|
|
15
|
+
TEST(ThumbhashDecode, DecodesKnownDimensions) {
|
|
16
|
+
const std::string hash = "1QcSHQRnh493V4dIh4eXh1h4kJUI";
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
auto [width, height, pixelArray] = decodeThumbhash(hash);
|
|
18
|
+
auto result = decodeThumbhash(hash);
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
EXPECT_EQ(result.width, 23);
|
|
21
|
+
EXPECT_EQ(result.height, 32);
|
|
22
|
+
ASSERT_NE(result.pixelArray, nullptr);
|
|
18
23
|
}
|
|
24
|
+
|
|
25
|
+
} // namespace
|
|
26
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#include "../../utils/Base64.h"
|
|
2
|
+
|
|
3
|
+
#include <gtest/gtest.h>
|
|
4
|
+
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
namespace expoimage {
|
|
8
|
+
namespace {
|
|
9
|
+
|
|
10
|
+
std::string decode(const std::string& in) {
|
|
11
|
+
std::string out;
|
|
12
|
+
EXPECT_TRUE(decodeBase64(in, out));
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
TEST(DecodeBase64, DecodesSvg) {
|
|
17
|
+
EXPECT_EQ(decode("PHN2Zy8+"), "<svg/>");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
TEST(DecodeBase64, EmptyInput) {
|
|
21
|
+
std::string out;
|
|
22
|
+
EXPECT_TRUE(decodeBase64("", out));
|
|
23
|
+
EXPECT_EQ(out, "");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
TEST(DecodeBase64, NoPadding) {
|
|
27
|
+
// "Man" -> "TWFu" (exact multiple of 4, no padding)
|
|
28
|
+
EXPECT_EQ(decode("TWFu"), "Man");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
TEST(DecodeBase64, OnePadByte) {
|
|
32
|
+
// "Ma" -> "TWE=" (one '=' -> two output bytes)
|
|
33
|
+
EXPECT_EQ(decode("TWE="), "Ma");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
TEST(DecodeBase64, TwoPadBytes) {
|
|
37
|
+
// "M" -> "TQ==" (two '=' -> one output byte)
|
|
38
|
+
EXPECT_EQ(decode("TQ=="), "M");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TEST(DecodeBase64, UnpaddedRemainder) {
|
|
42
|
+
// Same payloads as the padded cases but without '=' padding.
|
|
43
|
+
EXPECT_EQ(decode("TWE"), "Ma");
|
|
44
|
+
EXPECT_EQ(decode("TQ"), "M");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TEST(DecodeBase64, PlusAndSlash) {
|
|
48
|
+
// bytes {0xFB, 0xFF, 0xBF} -> "+/+/"
|
|
49
|
+
EXPECT_EQ(decode("+/+/"), std::string("\xFB\xFF\xBF", 3));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
TEST(DecodeBase64, RejectsInvalidCharacter) {
|
|
53
|
+
std::string out;
|
|
54
|
+
EXPECT_FALSE(decodeBase64("TW*u", out));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
TEST(DecodeBase64, RejectsWhitespace) {
|
|
58
|
+
std::string out;
|
|
59
|
+
EXPECT_FALSE(decodeBase64("TW Fu", out));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
TEST(DecodeBase64, RejectsDataAfterPadding) {
|
|
63
|
+
std::string out;
|
|
64
|
+
EXPECT_FALSE(decodeBase64("TQ==A", out));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
TEST(DecodeBase64, RejectsLoneRemainderByte) {
|
|
68
|
+
std::string out;
|
|
69
|
+
EXPECT_FALSE(decodeBase64("TWFuA", out));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
} // namespace
|
|
73
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#include "../../utils/ColorUtils.h"
|
|
2
|
+
|
|
3
|
+
#include <gtest/gtest.h>
|
|
4
|
+
|
|
5
|
+
namespace expoimage {
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
using Color = apmf::iface::com::amazon::kepler::uitoolkit::graphics::Color;
|
|
9
|
+
|
|
10
|
+
constexpr float kTolerance = 1.0f / 255.0f;
|
|
11
|
+
|
|
12
|
+
TEST(IntToColor, FullyOpaqueBlack) {
|
|
13
|
+
Color c = intToColor(0xFF000000);
|
|
14
|
+
EXPECT_NEAR(c.a, 1.0f, kTolerance);
|
|
15
|
+
EXPECT_NEAR(c.r, 0.0f, kTolerance);
|
|
16
|
+
EXPECT_NEAR(c.g, 0.0f, kTolerance);
|
|
17
|
+
EXPECT_NEAR(c.b, 0.0f, kTolerance);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
TEST(IntToColor, FullyOpaqueWhite) {
|
|
21
|
+
Color c = intToColor(0xFFFFFFFF);
|
|
22
|
+
EXPECT_NEAR(c.a, 1.0f, kTolerance);
|
|
23
|
+
EXPECT_NEAR(c.r, 1.0f, kTolerance);
|
|
24
|
+
EXPECT_NEAR(c.g, 1.0f, kTolerance);
|
|
25
|
+
EXPECT_NEAR(c.b, 1.0f, kTolerance);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
TEST(IntToColor, TransparentBlack) {
|
|
29
|
+
Color c = intToColor(0x00000000);
|
|
30
|
+
EXPECT_NEAR(c.a, 0.0f, kTolerance);
|
|
31
|
+
EXPECT_NEAR(c.r, 0.0f, kTolerance);
|
|
32
|
+
EXPECT_NEAR(c.g, 0.0f, kTolerance);
|
|
33
|
+
EXPECT_NEAR(c.b, 0.0f, kTolerance);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
TEST(IntToColor, ARGBChannelOrdering) {
|
|
37
|
+
Color c = intToColor(0x12345678);
|
|
38
|
+
EXPECT_NEAR(c.a, 0x12 / 255.0f, kTolerance);
|
|
39
|
+
EXPECT_NEAR(c.r, 0x34 / 255.0f, kTolerance);
|
|
40
|
+
EXPECT_NEAR(c.g, 0x56 / 255.0f, kTolerance);
|
|
41
|
+
EXPECT_NEAR(c.b, 0x78 / 255.0f, kTolerance);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
TEST(IntToColor, SignBitSetIsWellDefined) {
|
|
45
|
+
Color c = intToColor(static_cast<int>(0xFF112233u));
|
|
46
|
+
EXPECT_NEAR(c.a, 1.0f, kTolerance);
|
|
47
|
+
EXPECT_NEAR(c.r, 0x11 / 255.0f, kTolerance);
|
|
48
|
+
EXPECT_NEAR(c.g, 0x22 / 255.0f, kTolerance);
|
|
49
|
+
EXPECT_NEAR(c.b, 0x33 / 255.0f, kTolerance);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
} // namespace
|
|
53
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#include "../../utils/SourceSelection.h"
|
|
2
|
+
|
|
3
|
+
#include <gtest/gtest.h>
|
|
4
|
+
|
|
5
|
+
namespace expoimage {
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
using Size = apmf::iface::com::amazon::kepler::uitoolkit::graphics::Size;
|
|
9
|
+
|
|
10
|
+
// ---- startsWith (ASCII case-insensitive) ----
|
|
11
|
+
|
|
12
|
+
TEST(StartsWith, ExactPrefix) {
|
|
13
|
+
EXPECT_TRUE(startsWith("blurhash:abc", "blurhash"));
|
|
14
|
+
EXPECT_TRUE(startsWith("thumbhash:abc", "thumbhash"));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
TEST(StartsWith, CaseInsensitive) {
|
|
18
|
+
EXPECT_TRUE(startsWith("BlurHash:abc", "blurhash"));
|
|
19
|
+
EXPECT_TRUE(startsWith("BLURHASH", "blurhash"));
|
|
20
|
+
EXPECT_TRUE(startsWith("blurhash", "BLURHASH"));
|
|
21
|
+
EXPECT_TRUE(startsWith("ThumbHash:abc", "thumbhash"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
TEST(StartsWith, NoMatch) {
|
|
25
|
+
EXPECT_FALSE(startsWith("https://example.com", "blurhash"));
|
|
26
|
+
EXPECT_FALSE(startsWith("", "blurhash"));
|
|
27
|
+
EXPECT_FALSE(startsWith("blur", "blurhash")); // str shorter than prefix
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
TEST(StartsWith, EmptyPrefix) {
|
|
31
|
+
EXPECT_TRUE(startsWith("anything", ""));
|
|
32
|
+
EXPECT_TRUE(startsWith("", ""));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---- uriDecode ----
|
|
36
|
+
|
|
37
|
+
TEST(UriDecode, PassthroughPlainAscii) {
|
|
38
|
+
EXPECT_EQ(uriDecode("hello"), "hello");
|
|
39
|
+
EXPECT_EQ(uriDecode(""), "");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
TEST(UriDecode, DecodesHexEscapes) {
|
|
43
|
+
EXPECT_EQ(uriDecode("a%20b"), "a b");
|
|
44
|
+
EXPECT_EQ(uriDecode("%5E"), "^");
|
|
45
|
+
EXPECT_EQ(uriDecode("%2F"), "/");
|
|
46
|
+
EXPECT_EQ(uriDecode("%2f"), "/");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TEST(UriDecode, MixedContent) {
|
|
50
|
+
EXPECT_EQ(uriDecode("https://example.com/path%20with%20spaces?q=%3F"),
|
|
51
|
+
"https://example.com/path with spaces?q=?");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
TEST(UriDecode, MalformedEscapeIsLeftAsIs) {
|
|
55
|
+
EXPECT_EQ(uriDecode("50%"), "50%");
|
|
56
|
+
EXPECT_EQ(uriDecode("%ZZ"), "%ZZ");
|
|
57
|
+
EXPECT_EQ(uriDecode("%2"), "%2");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---- getBestSource ----
|
|
61
|
+
|
|
62
|
+
TEST(GetBestSource, EmptyReturnsNullopt) {
|
|
63
|
+
std::vector<ImageSourceInfo> sources;
|
|
64
|
+
auto result = getBestSource(sources, Size{100.0f, 100.0f});
|
|
65
|
+
EXPECT_FALSE(result.has_value());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
TEST(GetBestSource, SingleSourceIsAlwaysReturned) {
|
|
69
|
+
std::vector<ImageSourceInfo> sources{{"uri", 50.0f, 50.0f}};
|
|
70
|
+
auto result = getBestSource(sources, Size{100.0f, 100.0f});
|
|
71
|
+
ASSERT_TRUE(result.has_value());
|
|
72
|
+
EXPECT_EQ(result->uri, "uri");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
TEST(GetBestSource, PicksClosestPixelCountMatch) {
|
|
76
|
+
std::vector<ImageSourceInfo> sources{
|
|
77
|
+
{"small", 10.0f, 10.0f},
|
|
78
|
+
{"medium", 100.0f, 100.0f},
|
|
79
|
+
{"large", 500.0f, 500.0f}};
|
|
80
|
+
|
|
81
|
+
auto result = getBestSource(sources, Size{100.0f, 100.0f});
|
|
82
|
+
ASSERT_TRUE(result.has_value());
|
|
83
|
+
EXPECT_EQ(result->uri, "medium");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
} // namespace
|
|
87
|
+
} // namespace expoimage
|
|
@@ -7,12 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
#include "decode.h"
|
|
10
|
-
|
|
11
|
-
#include <mbedtls/base64.h>
|
|
10
|
+
#include "../utils/Base64.h"
|
|
12
11
|
|
|
13
12
|
#include <cmath>
|
|
14
13
|
#include <string>
|
|
15
|
-
#include <unordered_map>
|
|
16
14
|
#include <vector>
|
|
17
15
|
|
|
18
16
|
// Inspired by https://github.com/evanw/thumbhash/blob/main/java/com/madebyevan/thumbhash/ThumbHash.java
|
|
@@ -20,19 +18,12 @@
|
|
|
20
18
|
namespace {
|
|
21
19
|
|
|
22
20
|
std::string base64_decode(const std::string& in) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
mbedtls_base64_decode(
|
|
30
|
-
reinterpret_cast<unsigned char*>(buffer.data()),
|
|
31
|
-
bufferSize,
|
|
32
|
-
&bufferSize,
|
|
33
|
-
reinterpret_cast<const unsigned char*>(in.data()),
|
|
34
|
-
in.size());
|
|
35
|
-
|
|
21
|
+
std::string buffer;
|
|
22
|
+
if (!expoimage::decodeBase64(in, buffer)) {
|
|
23
|
+
// Return empty on malformed input so the size check in the caller
|
|
24
|
+
// rejects the hash instead of decoding a partial buffer into garbage pixels
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
36
27
|
return buffer;
|
|
37
28
|
}
|
|
38
29
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* base64 decoding.
|
|
3
|
+
*
|
|
4
|
+
* Adapted from base64.cpp by René Nyffenegger (zlib license). Altered from the
|
|
5
|
+
* original: decode-only, strict RFC 4648 validation, and a bool success return.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (C) 2004-2017 René Nyffenegger
|
|
8
|
+
*
|
|
9
|
+
* This source code is provided 'as-is', without any express or implied
|
|
10
|
+
* warranty. In no event will the author be held liable for any damages
|
|
11
|
+
* arising from the use of this software.
|
|
12
|
+
*
|
|
13
|
+
* Permission is granted to anyone to use this software for any purpose,
|
|
14
|
+
* including commercial applications, and to alter it and redistribute it
|
|
15
|
+
* freely, subject to the following restrictions:
|
|
16
|
+
*
|
|
17
|
+
* 1. The origin of this source code must not be misrepresented; you must not
|
|
18
|
+
* claim that you wrote the original source code. If you use this source code
|
|
19
|
+
* in a product, an acknowledgment in the product documentation would be
|
|
20
|
+
* appreciated but is not required.
|
|
21
|
+
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
22
|
+
* misrepresented as being the original source code.
|
|
23
|
+
* 3. This notice may not be removed or altered from any source distribution.
|
|
24
|
+
*
|
|
25
|
+
* René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
#include "Base64.h"
|
|
29
|
+
|
|
30
|
+
namespace expoimage {
|
|
31
|
+
|
|
32
|
+
namespace {
|
|
33
|
+
|
|
34
|
+
int decodeValue(unsigned char c) {
|
|
35
|
+
if (c >= 'A' && c <= 'Z') return c - 'A';
|
|
36
|
+
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
|
37
|
+
if (c >= '0' && c <= '9') return c - '0' + 52;
|
|
38
|
+
if (c == '+') return 62;
|
|
39
|
+
if (c == '/') return 63;
|
|
40
|
+
return -1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
} // namespace
|
|
44
|
+
|
|
45
|
+
bool decodeBase64(const std::string& input, std::string& output) {
|
|
46
|
+
output.clear();
|
|
47
|
+
output.reserve(input.size() / 4 * 3);
|
|
48
|
+
|
|
49
|
+
unsigned char block[4];
|
|
50
|
+
size_t filled = 0;
|
|
51
|
+
size_t padding = 0;
|
|
52
|
+
|
|
53
|
+
for (unsigned char c : input) {
|
|
54
|
+
if (c == '=') {
|
|
55
|
+
++padding;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (padding != 0) return false;
|
|
59
|
+
|
|
60
|
+
const int value = decodeValue(c);
|
|
61
|
+
if (value < 0) return false;
|
|
62
|
+
|
|
63
|
+
block[filled++] = static_cast<unsigned char>(value);
|
|
64
|
+
if (filled == 4) {
|
|
65
|
+
output.push_back(static_cast<char>((block[0] << 2) + ((block[1] & 0x30) >> 4)));
|
|
66
|
+
output.push_back(static_cast<char>(((block[1] & 0x0f) << 4) + ((block[2] & 0x3c) >> 2)));
|
|
67
|
+
output.push_back(static_cast<char>(((block[2] & 0x03) << 6) + block[3]));
|
|
68
|
+
filled = 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (padding > 2) return false;
|
|
73
|
+
if (filled == 1) return false;
|
|
74
|
+
|
|
75
|
+
if (filled >= 2) {
|
|
76
|
+
output.push_back(static_cast<char>((block[0] << 2) + ((block[1] & 0x30) >> 4)));
|
|
77
|
+
if (filled == 3) {
|
|
78
|
+
output.push_back(static_cast<char>(((block[1] & 0x0f) << 4) + ((block[2] & 0x3c) >> 2)));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* base64 decoding.
|
|
3
|
+
*
|
|
4
|
+
* Adapted from base64.cpp by René Nyffenegger (zlib license). Altered from the
|
|
5
|
+
* original: decode-only, strict RFC 4648 validation, and a bool success return.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (C) 2004-2017 René Nyffenegger
|
|
8
|
+
*
|
|
9
|
+
* This source code is provided 'as-is', without any express or implied
|
|
10
|
+
* warranty. In no event will the author be held liable for any damages
|
|
11
|
+
* arising from the use of this software.
|
|
12
|
+
*
|
|
13
|
+
* Permission is granted to anyone to use this software for any purpose,
|
|
14
|
+
* including commercial applications, and to alter it and redistribute it
|
|
15
|
+
* freely, subject to the following restrictions:
|
|
16
|
+
*
|
|
17
|
+
* 1. The origin of this source code must not be misrepresented; you must not
|
|
18
|
+
* claim that you wrote the original source code. If you use this source code
|
|
19
|
+
* in a product, an acknowledgment in the product documentation would be
|
|
20
|
+
* appreciated but is not required.
|
|
21
|
+
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
22
|
+
* misrepresented as being the original source code.
|
|
23
|
+
* 3. This notice may not be removed or altered from any source distribution.
|
|
24
|
+
*
|
|
25
|
+
* René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
#pragma once
|
|
29
|
+
|
|
30
|
+
#include <string>
|
|
31
|
+
|
|
32
|
+
namespace expoimage {
|
|
33
|
+
|
|
34
|
+
[[nodiscard]] bool decodeBase64(const std::string& input, std::string& output);
|
|
35
|
+
|
|
36
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
* Use is subject to license terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/Color.h>
|
|
11
|
+
|
|
12
|
+
#include <cstdint>
|
|
13
|
+
|
|
14
|
+
namespace expoimage {
|
|
15
|
+
|
|
16
|
+
/** Converts an ARGB integer (from processColor in JS) to a stable Color struct. */
|
|
17
|
+
inline apmf::iface::com::amazon::kepler::uitoolkit::graphics::Color intToColor(int argb) {
|
|
18
|
+
auto u = static_cast<uint32_t>(argb);
|
|
19
|
+
float a = ((u >> 24) & 0xFF) / 255.0f;
|
|
20
|
+
float r = ((u >> 16) & 0xFF) / 255.0f;
|
|
21
|
+
float g = ((u >> 8) & 0xFF) / 255.0f;
|
|
22
|
+
float b = (u & 0xFF) / 255.0f;
|
|
23
|
+
return {r, g, b, a};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
} // namespace expoimage
|