forked from organicmaps/organicmaps
Merge pull request #4250 from bykoianko/master-generating-route-altitude-chart-image-jni
[altitude chart] JNI Layer.
This commit is contained in:
commit
340dca5a06
6 changed files with 100 additions and 26 deletions
|
@ -5,6 +5,7 @@
|
|||
#include "com/mapswithme/opengl/androidoglcontextfactory.hpp"
|
||||
#include "com/mapswithme/platform/Platform.hpp"
|
||||
|
||||
#include "map/chart_generator.hpp"
|
||||
#include "map/user_mark.hpp"
|
||||
|
||||
#include "search/everywhere_search_params.hpp"
|
||||
|
@ -40,6 +41,8 @@ using namespace storage;
|
|||
using platform::CountryFile;
|
||||
using platform::LocalCountryFile;
|
||||
|
||||
static_assert(sizeof(int) >= 4, "Size of jint in less than 4 bytes.");
|
||||
|
||||
namespace
|
||||
{
|
||||
::Framework * frm()
|
||||
|
@ -891,6 +894,49 @@ Java_com_mapswithme_maps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jcl
|
|||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_com_mapswithme_maps_Framework_nativeGenerateRouteAltitudeChartBits(JNIEnv * env, jclass, jint width, jint height)
|
||||
{
|
||||
::Framework * fr = frm();
|
||||
ASSERT(fr, ());
|
||||
|
||||
vector<uint8_t> imageRGBAData;
|
||||
if (!fr->GenerateRouteAltitudeChart(width, height, imageRGBAData))
|
||||
{
|
||||
LOG(LWARNING, ("Can't generate route altitude image."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t const imageRGBADataSize = imageRGBAData.size();
|
||||
ASSERT_NOT_EQUAL(imageRGBADataSize, 0, ("GenerateRouteAltitudeChart returns true but the vector with altitude image bits is empty."));
|
||||
|
||||
size_t const pxlCount = width * height;
|
||||
if (maps::kAltitudeChartBPP * pxlCount != imageRGBADataSize)
|
||||
{
|
||||
LOG(LWARNING, ("Wrong size of vector with altitude image bits. Expected size:", pxlCount, ". Real size:", imageRGBADataSize));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jintArray imageRGBADataArray = env->NewIntArray(pxlCount);
|
||||
ASSERT(imageRGBADataArray, ());
|
||||
jint * arrayElements = env->GetIntArrayElements(imageRGBADataArray, 0);
|
||||
ASSERT(arrayElements, ());
|
||||
|
||||
for (size_t i = 0; i < pxlCount; ++i)
|
||||
{
|
||||
size_t const shiftInBytes = i * maps::kAltitudeChartBPP;
|
||||
// Type of |imageRGBAData| elements is uint8_t. But uint8_t is promoted to unsinged int in code below before shifting.
|
||||
// So there's no data lost in code below.
|
||||
arrayElements[i] = (imageRGBAData[shiftInBytes + 3] << 24) /* alpha */
|
||||
| (imageRGBAData[shiftInBytes] << 16) /* red */
|
||||
| (imageRGBAData[shiftInBytes + 1] << 8) /* green */
|
||||
| (imageRGBAData[shiftInBytes + 2]); /* blue */
|
||||
}
|
||||
env->ReleaseIntArrayElements(imageRGBADataArray, arrayElements, 0);
|
||||
|
||||
return imageRGBADataArray;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_mapswithme_maps_Framework_nativeShowCountry(JNIEnv * env, jclass, jstring countryId, jboolean zoomToDownloadButton)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.mapswithme.maps;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
@ -68,6 +69,25 @@ public class Framework
|
|||
return nativeGetGe0Url(lat, lon, zoomLevel, name).replaceFirst(Constants.Url.GE0_PREFIX, Constants.Url.HTTP_GE0_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Bitmap with route altitude image chart taking into account current map style.
|
||||
* @param width is width of the image.
|
||||
* @param height is height of the image.
|
||||
* @return Bitmap if there's pedestrian or bicycle route and null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public static Bitmap GenerateRouteAltitudeChart(int width, int height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
return null;
|
||||
|
||||
final int[] altitudeChartBits = Framework.nativeGenerateRouteAltitudeChartBits(width, height);
|
||||
if (altitudeChartBits == null)
|
||||
return null;
|
||||
|
||||
return Bitmap.createBitmap(altitudeChartBits, width, height, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
public static native void nativeShowTrackRect(int category, int track);
|
||||
|
||||
public static native int nativeGetDrawScale();
|
||||
|
@ -144,6 +164,9 @@ public class Framework
|
|||
@Nullable
|
||||
public static native RoutingInfo nativeGetRouteFollowingInfo();
|
||||
|
||||
@Nullable
|
||||
public static native final int[] nativeGenerateRouteAltitudeChartBits(int width, int height);
|
||||
|
||||
// When an end user is going to a turn he gets sound turn instructions.
|
||||
// If C++ part wants the client to pronounce an instruction nativeGenerateTurnNotifications returns
|
||||
// an array of one of more strings. C++ part assumes that all these strings shall be pronounced by the client's TTS.
|
||||
|
|
|
@ -174,18 +174,17 @@ bool GenerateYAxisChartData(uint32_t height, double minMetersPerPxl,
|
|||
return true;
|
||||
}
|
||||
|
||||
void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
|
||||
bool GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
|
||||
MapStyle mapStyle, vector<uint8_t> & frameBuffer)
|
||||
{
|
||||
frameBuffer.clear();
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
return false;
|
||||
|
||||
agg::rgba8 const kBackgroundColor = agg::rgba8(255, 255, 255, 0);
|
||||
agg::rgba8 const kLineColor = GetLineColor(mapStyle);
|
||||
agg::rgba8 const kCurveColor = GetCurveColor(mapStyle);
|
||||
double constexpr kLineWidthPxl = 2.0;
|
||||
uint32_t constexpr kBPP = 4;
|
||||
|
||||
using TBlender = BlendAdaptor<agg::rgba8, agg::order_rgba>;
|
||||
using TPixelFormat = agg::pixfmt_custom_blend_rgba<TBlender, agg::rendering_buffer>;
|
||||
|
@ -199,9 +198,9 @@ void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> c
|
|||
TPixelFormat pixelFormat(renderBuffer, agg::comp_op_src_over);
|
||||
TBaseRenderer baseRenderer(pixelFormat);
|
||||
|
||||
frameBuffer.assign(width * kBPP * height, 0);
|
||||
frameBuffer.assign(width * kAltitudeChartBPP * height, 0);
|
||||
renderBuffer.attach(&frameBuffer[0], static_cast<unsigned>(width),
|
||||
static_cast<unsigned>(height), static_cast<int>(width * kBPP));
|
||||
static_cast<unsigned>(height), static_cast<int>(width * kAltitudeChartBPP));
|
||||
|
||||
// Background.
|
||||
baseRenderer.reset_clipping(true);
|
||||
|
@ -214,7 +213,7 @@ void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> c
|
|||
rasterizer.clip_box(0, 0, width, height);
|
||||
|
||||
if (geometry.empty())
|
||||
return; /* No chart line to draw. */
|
||||
return true; /* No chart line to draw. */
|
||||
|
||||
// Polygon under chart line.
|
||||
agg::path_storage underChartGeometryPath;
|
||||
|
@ -238,6 +237,7 @@ void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> c
|
|||
|
||||
rasterizer.add_path(stroke);
|
||||
agg::render_scanlines_aa_solid(rasterizer, scanline, baseRenderer, kLineColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & distanceDataM,
|
||||
|
@ -270,7 +270,6 @@ bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & dista
|
|||
geometry[i] = m2::PointD(i * oneSegLenPix, yAxisDataPxl[i]);
|
||||
}
|
||||
|
||||
GenerateChartByPoints(width, height, geometry, mapStyle, frameBuffer);
|
||||
return true;
|
||||
return GenerateChartByPoints(width, height, geometry, mapStyle, frameBuffer);
|
||||
}
|
||||
} // namespace maps
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace maps
|
||||
{
|
||||
uint32_t constexpr kAltitudeChartBPP = 4;
|
||||
|
||||
/// \brief fills uniformAltitudeDataM with altitude data which evenly distributed by
|
||||
/// |resultPointCount| points. |distanceDataM| and |altitudeDataM| form a curve of route altitude.
|
||||
/// This method is used to generalize and evenly distribute points of the chart.
|
||||
|
@ -34,7 +36,7 @@ bool GenerateYAxisChartData(uint32_t height, double minMetersPerPxl,
|
|||
/// \param mapStyle is a current map style.
|
||||
/// \param frameBuffer is a vector for a result image. It's resized in this method.
|
||||
/// It's filled with RGBA(8888) image date.
|
||||
void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
|
||||
bool GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
|
||||
MapStyle mapStyle, vector<uint8_t> & frameBuffer);
|
||||
|
||||
bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & distanceDataM,
|
||||
|
|
|
@ -729,7 +729,9 @@ public:
|
|||
/// false otherwise.
|
||||
bool HasRouteAltitude() const;
|
||||
/// \brief Generates 4 bytes per point image (RGBA) and put the data to |imageRGBAData|.
|
||||
/// \returns If there is valid route info and returns true and false otherwise.
|
||||
/// \returns If there is valid route info and the chart was generated returns true
|
||||
/// and false otherwise. If the method returns true it is guaranteed that the size of
|
||||
/// |imageRGBAData| is not zero.
|
||||
/// \note If HasRouteAltitude() method returns true, GenerateRouteAltitudeChart(...)
|
||||
/// could return false if route was deleted or rebuilt between the calls.
|
||||
bool GenerateRouteAltitudeChart(uint32_t width, uint32_t height,
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
namespace
|
||||
{
|
||||
double constexpr kEpsilon = 0.00001;
|
||||
uint32_t constexpr kBPP = 4;
|
||||
|
||||
using namespace maps;
|
||||
|
||||
bool AlmostEqualAbs(vector<double> const & v1, vector<double> const & v2)
|
||||
{
|
||||
|
@ -27,7 +28,7 @@ bool AlmostEqualAbs(vector<double> const & v1, vector<double> const & v2)
|
|||
bool IsColor(vector<uint8_t> const & frameBuffer, size_t startColorIdx, uint8_t expectedR,
|
||||
uint8_t expectedG, uint8_t expectedB, uint8_t expectedA)
|
||||
{
|
||||
CHECK_LESS_OR_EQUAL(startColorIdx + kBPP, frameBuffer.size(), ());
|
||||
CHECK_LESS_OR_EQUAL(startColorIdx + kAltitudeChartBPP, frameBuffer.size(), ());
|
||||
|
||||
return frameBuffer[startColorIdx] == expectedR && frameBuffer[startColorIdx + 1] == expectedG &&
|
||||
frameBuffer[startColorIdx + 2] == expectedB && frameBuffer[startColorIdx + 3] == expectedA;
|
||||
|
@ -36,13 +37,13 @@ bool IsColor(vector<uint8_t> const & frameBuffer, size_t startColorIdx, uint8_t
|
|||
void TestAngleColors(size_t width, size_t height, vector<uint8_t> const & frameBuffer,
|
||||
uint8_t expectedR, uint8_t expectedG, uint8_t expectedB, uint8_t expectedA)
|
||||
{
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kBPP, ());
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kAltitudeChartBPP, ());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, expectedR, expectedG, expectedB, expectedA), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * (width - 1) /* startColorIdx */, expectedR,
|
||||
TEST(IsColor(frameBuffer, kAltitudeChartBPP * (width - 1) /* startColorIdx */, expectedR,
|
||||
expectedG, expectedB, expectedA), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * height * (width - 1) /* startColorIdx */,
|
||||
TEST(IsColor(frameBuffer, kAltitudeChartBPP * height * (width - 1) /* startColorIdx */,
|
||||
expectedR, expectedG, expectedB, expectedA), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * height * width - kBPP /* startColorIdx */,
|
||||
TEST(IsColor(frameBuffer, kAltitudeChartBPP * height * width - kAltitudeChartBPP /* startColorIdx */,
|
||||
expectedR, expectedG, expectedB, expectedA), ());
|
||||
}
|
||||
|
||||
|
@ -133,7 +134,7 @@ UNIT_TEST(GenerateChartByPoints_NoGeometryTest)
|
|||
size_t constexpr height = 40;
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer);
|
||||
TEST(maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer), ());
|
||||
TestAngleColors(width, height, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */);
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ UNIT_TEST(GenerateChartByPoints_OnePointTest)
|
|||
size_t constexpr height = 40;
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer);
|
||||
TEST(maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer), ());
|
||||
TestAngleColors(width, height, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */);
|
||||
}
|
||||
|
@ -158,13 +159,13 @@ UNIT_TEST(GenerateChartByPoints_Test)
|
|||
size_t constexpr height = 40;
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer);
|
||||
TEST(maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer), ());
|
||||
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kBPP, ());
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kAltitudeChartBPP, ());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 30 /* expectedR */, 150 /* expectedG */,
|
||||
240 /* expectedB */, 255 /* expectedA */),
|
||||
());
|
||||
TEST(IsColor(frameBuffer, kBPP * (width - 1) /* startColorIdx */, 255 /* expectedR */,
|
||||
TEST(IsColor(frameBuffer, kAltitudeChartBPP * (width - 1) /* startColorIdx */, 255 /* expectedR */,
|
||||
255 /* expectedG */, 255 /* expectedB */, 0 /* expectedA */),
|
||||
());
|
||||
}
|
||||
|
@ -194,10 +195,10 @@ UNIT_TEST(GenerateChart_OnePointTest)
|
|||
TEST(maps::GenerateChart(width, height, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
());
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kBPP, ());
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kAltitudeChartBPP, ());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * (width - 1) /* startColorIdx */, 255 /* expectedR */,
|
||||
TEST(IsColor(frameBuffer, kAltitudeChartBPP * (width - 1) /* startColorIdx */, 255 /* expectedR */,
|
||||
255 /* expectedG */, 255 /* expectedB */, 0 /* expectedA */), ());
|
||||
}
|
||||
|
||||
|
@ -208,8 +209,8 @@ UNIT_TEST(GenerateChart_EmptyRectTest)
|
|||
feature::TAltitudes const & altitudeDataM = {};
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
TEST(maps::GenerateChart(width, 50 /* height */, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
TEST(!maps::GenerateChart(width, 50 /* height */, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
());
|
||||
TEST(frameBuffer.empty(), ());
|
||||
}
|
||||
|
@ -227,7 +228,8 @@ UNIT_TEST(GenerateChart_Test)
|
|||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */),
|
||||
());
|
||||
TEST(IsColor(frameBuffer, kBPP * 3 * width - kBPP /* startColorIdx */, 255 /* expectedR */,
|
||||
TEST(IsColor(frameBuffer, kAltitudeChartBPP * 3 * width -
|
||||
kAltitudeChartBPP /* startColorIdx */, 255 /* expectedR */,
|
||||
230 /* expectedG */, 140 /* expectedB */, 255 /* expectedA */),
|
||||
());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue