Merge pull request #4250 from bykoianko/master-generating-route-altitude-chart-image-jni

[altitude chart] JNI Layer.
This commit is contained in:
ygorshenin 2016-09-13 16:58:18 +03:00 committed by GitHub
commit 340dca5a06
6 changed files with 100 additions and 26 deletions

View file

@ -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)
{

View file

@ -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.

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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 */),
());
}