PointI C_Window::GetCursorPos()
{
	POINT pt;
	if(::GetCursorPos(&pt))
	{
		return PointI(pt.x, pt.y);
	}
	return PointI(0, 0);
}
PointI C_Window::GetPosition()
{
	RECT rect;
	if(GetWindowRect(m_handle, &rect))
	{
		return PointI(rect.left, rect.top);
	}
	return PointI(0,0);
}
예제 #3
0
// Going to a bookmark within current file scrolls to a given page.
// Going to a bookmark in another file, loads the file and scrolls to a page
// (similar to how invoking one of the recently opened files works)
static void GoToFavorite(WindowInfo* win, DisplayState* f, Favorite* fn) {
    CrashIf(!f || !fn);
    if (!f || !fn) {
        return;
    }

    WindowInfo* existingWin = FindWindowInfoByFile(f->filePath, true);
    if (existingWin) {
        int pageNo = fn->pageNo;
        uitask::Post([=] { GoToFavorite(existingWin, pageNo); });
        return;
    }

    if (!HasPermission(Perm_DiskAccess)) {
        return;
    }

    // When loading a new document, go directly to selected page instead of
    // first showing last seen page stored in file history
    // A hacky solution because I don't want to add even more parameters to
    // LoadDocument() and LoadDocumentInto()
    int pageNo = fn->pageNo;
    DisplayState* ds = gFileHistory.Find(f->filePath);
    if (ds && !ds->useDefaultState && gGlobalPrefs->rememberStatePerDocument) {
        ds->pageNo = fn->pageNo;
        ds->scrollPos = PointI(-1, -1); // don't scroll the page
        pageNo = -1;
    }

    LoadArgs args(f->filePath, win);
    win = LoadDocument(args);
    if (win) {
        uitask::Post([=] { (win, pageNo); });
    }
}
예제 #4
0
bool IsCursorOverWindow(HWND hwnd)
{
    POINT pt;
    GetCursorPos(&pt);
    WindowRect rcWnd(hwnd);
    return rcWnd.Contains(PointI(pt.x, pt.y));
}
예제 #5
0
OsmAnd::Model::Road::Road( const std::shared_ptr<Road>& that, int insertIdx, uint32_t x31, uint32_t y31 )
    : _ref(that)
    , _id(0)
    , _points(_ref->_points.size() + 1)
    , subsection(_ref->subsection)
    , id(_ref->_id)
    , names(_ref->_names)
    , points(_points)
    , types(_ref->_types)
    , pointsTypes(_pointsTypes)
    , restrictions(_ref->_restrictions)
{
    int pointIdx = 0;
    for(; pointIdx < insertIdx; pointIdx++)
    {
        _points[pointIdx] = _ref->_points[pointIdx];
        if(!_ref->_pointsTypes.isEmpty())
            _pointsTypes.insert(pointIdx, _ref->_pointsTypes[pointIdx]);
    }
    _points[pointIdx] = PointI(x31, y31);
    for(pointIdx += 1; pointIdx < _points.size(); pointIdx++)
    {
        _points[pointIdx] = _ref->_points[pointIdx - 1];
        if(!_ref->_pointsTypes.isEmpty())
            _pointsTypes.insert(pointIdx, _ref->_pointsTypes[pointIdx]);
    }
}
예제 #6
0
static LRESULT CALLBACK WndProcFavBox(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    WindowInfo* win = FindWindowInfoByHwnd(hwnd);
    if (!win)
        return CallWindowProc(DefWndProcFavBox, hwnd, message, wParam, lParam);
    switch (message) {
        case WM_SIZE:
            LayoutTreeContainer(win->favLabelWithClose, win->hwndFavTree);
            break;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDC_FAV_LABEL_WITH_CLOSE)
                ToggleFavorites(win);
            break;

        case WM_NOTIFY:
            if (LOWORD(wParam) == IDC_FAV_TREE) {
                LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
                LRESULT res = OnFavTreeNotify(win, pnmtv);
                if (res != -1)
                    return res;
            }
            break;

        case WM_CONTEXTMENU:
            if (win->hwndFavTree == (HWND)wParam) {
                OnFavTreeContextMenu(win, PointI(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
                return 0;
            }
            break;
    }
    return CallWindowProc(DefWndProcFavBox, hwnd, message, wParam, lParam);
}
예제 #7
0
TerrainRenderer::PointF TerrainRenderer::GetBAround(const MapPoint pt, const unsigned char triangle, const unsigned char dir)
{
    PointI ptNb = GetPointAround(PointI(pt), dir);

    Point<int> offset;
    MapPoint t = ConvertCoords(ptNb, &offset);

    return GetB(t, triangle) + PointF(offset);
}
예제 #8
0
TerrainRenderer::PointF TerrainRenderer::GetTerrainAround(MapPoint pt, const unsigned dir)
{
    PointI ptNb = GetPointAround(PointI(pt), dir);

    PointI offset;
    MapPoint t = ConvertCoords(ptNb, &offset);

    return GetTerrain(t) + PointF(offset);
}
예제 #9
0
PointI OsmAndMapView::getPixelPoint(int32_t x31, int32_t y31) const{
    int cx = getCenterPointX();
    int cy = getCenterPointY();
    auto pw = OsmAnd::Utilities::getPowZoom(31 - getZoom());
    float diffxTile = x31 / pw - getXTile();
    float diffyTile = y31 / pw - getYTile();
    int newX = (int) (calcDiffPixelX(diffxTile, diffyTile) + cx);
    int newY = (int) (calcDiffPixelY(diffxTile, diffyTile) + cy);
    return PointI(newX, newY);
}
예제 #10
0
bool GetCursorPosInHwnd(HWND hwnd, PointI& posOut)
{
    POINT pt;
    if (!GetCursorPos(&pt))
        return false;
    if (!ScreenToClient(hwnd, &pt))
        return false;
    posOut = PointI(pt.x, pt.y);
    return true;
}
// Actual window procedure that will handle saving window size/position and moving
// the controls whilst the window sizes.
static LRESULT CALLBACK SizingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    DialogData *pdd = (DialogData *)GetProp(hwnd, DIALOG_DATA_PROPERTY);
    if (!pdd)
        return DefWindowProc(hwnd, msg, wParam, lParam);

    switch (msg)
    {
        case WM_ERASEBKGND:
        {
            LRESULT lr = CallWindowProc(pdd->wndProc, hwnd, msg, wParam, lParam);
            pdd->DrawGripper((HDC)wParam);
            return lr;
        }

        case WM_SIZE:
        {
            if (wParam != SIZE_MINIMIZED)
            {
                pdd->bMaximised = wParam == SIZE_MAXIMIZED;
                UpdateWindowSize(pdd, LOWORD(lParam), HIWORD(lParam), hwnd);
            }
        }
        break;

        case WM_NCHITTEST:
        {
            // If the gripper is enabled then perform a simple hit test on our gripper area.
            POINT pt = { LOWORD(lParam), HIWORD(lParam) };
            ScreenToClient(hwnd, &pt);
            if (pdd->InsideGripper(PointI(pt.x, pt.y)))
                return HTBOTTOMRIGHT;
        }
        break;

        case WM_GETMINMAXINFO:
        {
            // Our opportunity to say that we do not want the dialog to grow or shrink any more.
            LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
            lpmmi->ptMinTrackSize = pdd->ptSmallest;
        }
        return 0;

        case WM_DESTROY:
        {
            WNDPROC wndProc = pdd->wndProc;
            delete pdd;
            return CallWindowProc(wndProc, hwnd, msg, wParam, lParam);
        }
    }

    return CallWindowProc(pdd->wndProc, hwnd, msg, wParam, lParam);
}
예제 #12
0
void ChmModel::UpdateDisplayState(DisplayState *ds)
{
    if (!ds->filePath || !str::EqI(ds->filePath, fileName))
        str::ReplacePtr(&ds->filePath, fileName);

    ds->useDefaultState = !gGlobalPrefs->rememberStatePerDocument;

    str::ReplacePtr(&ds->displayMode, prefs::conv::FromDisplayMode(GetDisplayMode()));
    prefs::conv::FromZoom(&ds->zoom, GetZoomVirtual(), ds);

    ds->pageNo = CurrentPageNo();
    ds->scrollPos = PointI();
}
static bool IsTileVisible(DisplayModel *dm, int pageNo, int rotation, float zoom, TilePosition tile, float fuzz=0)
{
    if (!dm) return false;
    PageInfo *pageInfo = dm->GetPageInfo(pageNo);
    if (!dm->engine || !pageInfo) return false;
    RectI tileOnScreen = GetTileOnScreen(dm->engine, pageNo, rotation, zoom, tile, pageInfo->pageOnScreen);
    // consider nearby tiles visible depending on the fuzz factor
    tileOnScreen.x -= (int)(tileOnScreen.dx * fuzz * 0.5);
    tileOnScreen.dx = (int)(tileOnScreen.dx * (fuzz + 1));
    tileOnScreen.y -= (int)(tileOnScreen.dy * fuzz * 0.5);
    tileOnScreen.dy = (int)(tileOnScreen.dy * (fuzz + 1));
    RectI screen(PointI(), dm->viewPort.Size());
    return !tileOnScreen.Intersect(screen).IsEmpty();
}
예제 #14
0
LRESULT CALLBACK NotificationWnd::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    NotificationWnd *wnd = (NotificationWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (WM_ERASEBKGND == msg) {
        // do nothing, helps to avoid flicker
        return TRUE;
    }

    if (WM_TIMER == msg && TIMEOUT_TIMER_ID == wParam) {
        if (wnd->notificationCb)
            wnd->notificationCb->RemoveNotification(wnd);
        else
            delete wnd;
        return 0;
    }

    if (WM_PAINT == msg && wnd) {
        NotificationWndOnPaint(hwnd, wnd);
        return 0;
    }

    if (WM_SETCURSOR == msg && wnd->hasCancel) {
        PointI pt;
        if (GetCursorPosInHwnd(hwnd, pt) &&
            GetCancelRect(hwnd).Contains(pt)) {
            SetCursor(IDC_HAND);
            return TRUE;
        }
    }

    if (WM_LBUTTONUP == msg && wnd->hasCancel) {
        if (GetCancelRect(hwnd).Contains(PointI(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))) {
            if (wnd->notificationCb)
                wnd->notificationCb->RemoveNotification(wnd);
            else
                delete wnd;
            return 0;
        }
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}
예제 #15
0
void Map::updateNodes()
{
	for (int x = 0; x < m_worldWidthTiles; ++x)
	{
		for (int y = 0; y < m_worldHeightTiles; ++y)
		{
			TileNode& tileNode = m_tileNodes[getTileNodeIndex(x, y)];
			tileNode.m_type = getTileType(x, y);
			tileNode.m_pos = PointI(x, y);
			tileNode.m_transition = TileNode::Transition();
			tileNode.m_isWaypoint = false;
			// TODO - cars, etc...
		}
	}

	for (const auto& waypoint : m_world->getWaypoints())
	{
		TileNode& tileNode = m_tileNodes[getTileNodeIndex(waypoint[0], waypoint[1])];
		tileNode.m_isWaypoint = true;
	}
}
예제 #16
0
OsmAnd::MapRenderer::MapRenderer()
    : _taskHostBridge(this)
    , currentConfiguration(_currentConfiguration)
    , _currentConfigurationInvalidatedMask(0xFFFFFFFF)
    , currentState(_currentState)
    , _currentStateInvalidated(true)
    , _invalidatedRasterLayerResourcesMask(0)
    , _invalidatedElevationDataResources(false)
    , tiledResources(_tiledResources)
    , _renderThreadId(nullptr)
    , _workerThreadId(nullptr)
    , renderAPI(_renderAPI)
{
    _tileRequestsThreadPool.setMaxThreadCount(4);

    // Create all tiled resources
    for(auto resourceType = 0u; resourceType < TiledResourceTypesCount; resourceType++)
    {
        auto collection = new TiledResources(static_cast<TiledResourceType>(resourceType));
        _tiledResources[resourceType].reset(collection);
    }

    // Fill-up default state
    for(auto layerId = 0u; layerId < RasterMapLayersCount; layerId++)
        setRasterLayerOpacity(static_cast<RasterMapLayerId>(layerId), 1.0f);
    setElevationDataScaleFactor(1.0f, true);
    setFieldOfView(16.5f, true);
    setDistanceToFog(400.0f, true);
    setFogOriginFactor(0.36f, true);
    setFogHeightOriginFactor(0.05f, true);
    setFogDensity(1.9f, true);
    setFogColor(FColorRGB(1.0f, 0.0f, 0.0f), true);
    setSkyColor(FColorRGB(140.0f / 255.0f, 190.0f / 255.0f, 214.0f / 255.0f), true);
    setAzimuth(0.0f, true);
    setElevationAngle(45.0f, true);
    const auto centerIndex = 1u << (ZoomLevel::MaxZoomLevel - 1);
    setTarget(PointI(centerIndex, centerIndex), true);
    setZoom(0, true);
}
void OsmAnd::ObfRoutingSectionReader_P::readRoad(
    const std::unique_ptr<ObfReader_P>& reader, const std::shared_ptr<const ObfRoutingSubsectionInfo>& subsection,
    const QList<uint64_t>& idsTable, uint32_t& internalId, const std::shared_ptr<Model::Road>& road )
{
    auto cis = reader->_codedInputStream.get();
    for(;;)
    {
        auto tag = cis->ReadTag();
        switch(gpb::internal::WireFormatLite::GetTagFieldNumber(tag))
        {
        case 0:
            return;
        case OBF::RouteData::kPointsFieldNumber:
            {
                gpb::uint32 length;
                cis->ReadVarint32(&length);
                auto oldLimit = cis->PushLimit(length);
                auto dx = subsection->_area31.left >> ShiftCoordinates;
                auto dy = subsection->_area31.top >> ShiftCoordinates;
                while(cis->BytesUntilLimit() > 0)
                {
                    const uint32_t x = ObfReaderUtilities::readSInt32(cis) + dx;
                    const uint32_t y = ObfReaderUtilities::readSInt32(cis) + dy;

                    road->_points.push_back(qMove(PointI(
                        x << ShiftCoordinates,
                        y << ShiftCoordinates
                        )));

                    dx = x;
                    dy = y;
                }
                cis->PopLimit(oldLimit);
            }
            break;
        case OBF::RouteData::kPointTypesFieldNumber:
            {
                gpb::uint32 length;
                cis->ReadVarint32(&length);
                auto oldLimit = cis->PushLimit(length);
                while(cis->BytesUntilLimit() > 0)
                {
                    gpb::uint32 pointIdx;
                    cis->ReadVarint32(&pointIdx);

                    gpb::uint32 innerLength;
                    cis->ReadVarint32(&innerLength);
                    auto innerOldLimit = cis->PushLimit(innerLength);

                    auto& pointTypes = road->_pointsTypes.insert(pointIdx, QVector<uint32_t>()).value();
                    while(cis->BytesUntilLimit() > 0)
                    {
                        gpb::uint32 pointType;
                        cis->ReadVarint32(&pointType);
                        pointTypes.push_back(pointType);
                    }
                    cis->PopLimit(innerOldLimit);
                }
                cis->PopLimit(oldLimit);
            }
            break;
        case OBF::RouteData::kTypesFieldNumber:
            {
                gpb::uint32 length;
                cis->ReadVarint32(&length);
                auto oldLimit = cis->PushLimit(length);
                while(cis->BytesUntilLimit() > 0)
                {
                    gpb::uint32 type;
                    cis->ReadVarint32(&type);
                    road->_types.push_back(type);
                }
                cis->PopLimit(oldLimit);
            }
            break;
        case OBF::RouteData::kRouteIdFieldNumber:
            {
                gpb::uint32 id;
                cis->ReadVarint32(&id);
                internalId = id;
                if(id < idsTable.size())
                    road->_id = idsTable[id];
                else
                    road->_id = id;
            }
            break;
        case OBF::RouteData::kStringNamesFieldNumber:
            {
                gpb::uint32 length;
                cis->ReadVarint32(&length);
                auto oldLimit = cis->PushLimit(length);
                while(cis->BytesUntilLimit() > 0)
                {
                    gpb::uint32 stringTag;
                    cis->ReadVarint32(&stringTag);
                    gpb::uint32 stringId;
                    cis->ReadVarint32(&stringId);

                    road->_names.insert(stringTag, ObfReaderUtilities::encodeIntegerToString(stringId));
                }
                cis->PopLimit(oldLimit);
            }
            break;
        default:
            ObfReaderUtilities::skipUnknownField(cis, tag);
            break;
        }
    }
}
예제 #18
0
// -scroll x,y
static void ParseScrollValue(PointI *scroll, const WCHAR *txt)
{
    int x, y;
    if (str::Parse(txt, L"%d,%d%$", &x, &y))
        *scroll = PointI(x, y);
}
void OsmAnd::RoutePlanner::attachRouteSegments(
    OsmAnd::RoutePlannerContext::CalculationContext* context,
    const QVector< std::shared_ptr<RouteSegment> >& route,
    const QVector< std::shared_ptr<RouteSegment> >::const_iterator& itSegment,
    uint32_t pointIdx, bool isIncrement)
{
    const auto& segment = *itSegment;
    const auto& nextL = pointIdx < segment->road->points.size() - 1
        ? segment->road->points[pointIdx + 1]
        : PointI();
    const auto& prevL = pointIdx > 0
        ? segment->road->points[pointIdx - 1]
        : PointI();

    // by default make same as this road id
    auto previousRoadId = segment->road->id;
    if (pointIdx == segment->startPointIndex && itSegment != route.cbegin())
    {
        // attach additional roads to represent more information about the route
        const auto& previousResult = *(itSegment - 1);
        previousRoadId = previousResult->road->id;
        if (previousRoadId != segment->road->id)
        {
            std::shared_ptr<RouteSegment> attachedSegment;

            if (previousResult->startPointIndex < previousResult->endPointIndex &&
                previousResult->endPointIndex < previousResult->road->points.size() - 1)
                attachedSegment.reset(new RouteSegment(previousResult->road, previousResult->endPointIndex, previousResult->road->points.size() - 1));
            else if (previousResult->startPointIndex > previousResult->endPointIndex && previousResult->endPointIndex > 0)
                attachedSegment.reset(new RouteSegment(previousResult->road, previousResult->endPointIndex, 0));

            if (attachedSegment)
                segment->_attachedRoutes[qAbs(static_cast<int64_t>(pointIdx) - static_cast<int64_t>(segment->_startPointIndex))].push_back(qMove(attachedSegment));
        }
    }

    // Try to attach all segments except with current id
    const auto& p31 = segment->road->points[pointIdx];
    auto rt = OsmAnd::RoutePlanner::loadRouteCalculationSegment(context->owner, p31.x, p31.y);
    while(rt)
    {
        if (rt->road->id != segment->road->id && rt->road->id != previousRoadId)
        {
            std::shared_ptr<RouteSegment> attachedSegment;
            //TODO:GC:checkAndInitRouteRegion(ctx, rt->road);
            // TODO restrictions can be considered as well
            auto direction = context->owner->profileContext->getDirection(rt->road);
            if ((direction == Model::RoadDirection::TwoWay || direction == Model::RoadDirection::OneWayReverse) && rt->pointIndex < rt->road->points.size() - 1)
            {
                const auto& otherPoint = rt->road->points[rt->pointIndex + 1];
                if (otherPoint != nextL && otherPoint != prevL)
                {
                    // if way contains same segment (nodes) as different way (do not attach it)
                    attachedSegment.reset(new RouteSegment(rt->road, rt->pointIndex, rt->road->points.size() - 1));
                }
            }
            if ((direction == Model::RoadDirection::TwoWay || direction == Model::RoadDirection::OneWayForward) && rt->pointIndex > 0)
            {
                const auto& otherPoint = rt->road->points[rt->pointIndex - 1];
                if (otherPoint != nextL && otherPoint != prevL)
                {
                    // if way contains same segment (nodes) as different way (do not attach it)
                    attachedSegment.reset(new RouteSegment(rt->road, rt->pointIndex, 0));
                }
            }

            if (attachedSegment)
                segment->_attachedRoutes[qAbs(static_cast<int64_t>(pointIdx) - static_cast<int64_t>(segment->_startPointIndex))].push_back(qMove(attachedSegment));
        }

        rt = rt->next;
    }
}
예제 #20
0
void OsmAnd::MapRasterizer_P::rasterizePolygon(
    const Context& context,
    SkCanvas& canvas,
    const std::shared_ptr<const MapPrimitiviser::Primitive>& primitive)
{
    const auto& points31 = primitive->sourceObject->points31;
    const auto& area31 = context.area31;

    assert(points31.size() > 2);
    assert(primitive->sourceObject->isClosedFigure());
    assert(primitive->sourceObject->isClosedFigure(true));

    //////////////////////////////////////////////////////////////////////////
    //if ((primitive->sourceObject->id >> 1) == 9223372032559801460u)
    //{
    //    int i = 5;
    //}
    //////////////////////////////////////////////////////////////////////////

    SkPaint paint = _defaultPaint;
    if (!updatePaint(context, paint, primitive->evaluationResult, PaintValuesSet::Layer_1, true))
        return;

    // Construct and test geometry against bbox area
    SkPath path;
    bool containsAtLeastOnePoint = false;
    int pointIdx = 0;
    PointF vertex;
    Utilities::CHValue prevChValue;
    QVector< PointI > outerPoints;
    const auto pointsCount = points31.size();
    auto pPoint = points31.constData();
    for (auto pointIdx = 0; pointIdx < pointsCount; pointIdx++, pPoint++)
    {
        const auto& point = *pPoint;
        calculateVertex(context, point, vertex);

        // Hit-test
        if (!containsAtLeastOnePoint)
        {
            if (area31.contains(point))
                containsAtLeastOnePoint = true;
            else
                outerPoints.push_back(point);

            const auto chValue = Utilities::computeCohenSutherlandValue(point, area31);
            if (Q_LIKELY(pointIdx > 0))
            {
                // Check if line crosses area (reject only if points are on the same side)
                const auto intersectedChValue = prevChValue & chValue;
                if (static_cast<unsigned int>(intersectedChValue) != 0)
                    containsAtLeastOnePoint = true;
            }
            prevChValue = chValue;
        }

        // Plot vertex
        if (pointIdx == 0)
            path.moveTo(vertex.x, vertex.y);
        else
            path.lineTo(vertex.x, vertex.y);
    }

    //////////////////////////////////////////////////////////////////////////
    //if ((primitive->sourceObject->id >> 1) == 9223372032559801460u)
    //{
    //    int i = 5;
    //}
    //////////////////////////////////////////////////////////////////////////

    if (!containsAtLeastOnePoint)
    {
        // Check area is inside polygon
        bool ok = true;
        ok = ok || containsHelper(outerPoints, area31.topLeft);
        ok = ok || containsHelper(outerPoints, area31.bottomRight);
        ok = ok || containsHelper(outerPoints, PointI(0, area31.bottom()));
        ok = ok || containsHelper(outerPoints, PointI(area31.right(), 0));
        if (!ok)
            return;
    }

    //////////////////////////////////////////////////////////////////////////
    //if ((primitive->sourceObject->id >> 1) == 95692962u)
    //{
    //    int i = 5;
    //}
    //////////////////////////////////////////////////////////////////////////

    if (!primitive->sourceObject->innerPolygonsPoints31.isEmpty())
    {
        path.setFillType(SkPath::kEvenOdd_FillType);
        for (const auto& polygon : constOf(primitive->sourceObject->innerPolygonsPoints31))
        {
            pointIdx = 0;
            for (auto itVertex = cachingIteratorOf(constOf(polygon)); itVertex; ++itVertex, pointIdx++)
            {
                const auto& point = *itVertex;
                calculateVertex(context, point, vertex);

                if (pointIdx == 0)
                    path.moveTo(vertex.x, vertex.y);
                else
                    path.lineTo(vertex.x, vertex.y);
            }
        }
    }

    canvas.drawPath(path, paint);
    if (updatePaint(context, paint, primitive->evaluationResult, PaintValuesSet::Layer_2, false))
        canvas.drawPath(path, paint);
}
bool OsmAnd::MapPrimitivesProvider_P::obtainData(
    const TileId tileId,
    const ZoomLevel zoom,
    std::shared_ptr<MapPrimitivesProvider::Data>& outTiledData,
    MapPrimitivesProvider_Metrics::Metric_obtainData* const metric_,
    const IQueryController* const queryController)
{
#if OSMAND_PERFORMANCE_METRICS
    MapPrimitivesProvider_Metrics::Metric_obtainData localMetric;
    const auto metric = metric_ ? metric_ : &localMetric;
#else
    const auto metric = metric_;
#endif

    const Stopwatch totalStopwatch(metric != nullptr);

    std::shared_ptr<TileEntry> tileEntry;

    for (;;)
    {
        // Try to obtain previous instance of tile
        _tileReferences.obtainOrAllocateEntry(tileEntry, tileId, zoom,
            []
            (const TiledEntriesCollection<TileEntry>& collection, const TileId tileId, const ZoomLevel zoom) -> TileEntry*
            {
                return new TileEntry(collection, tileId, zoom);
            });

        // If state is "Undefined", change it to "Loading" and proceed with loading
        if (tileEntry->setStateIf(TileState::Undefined, TileState::Loading))
            break;

        // In case tile entry is being loaded, wait until it will finish loading
        if (tileEntry->getState() == TileState::Loading)
        {
            QReadLocker scopedLcoker(&tileEntry->loadedConditionLock);

            // If tile is in 'Loading' state, wait until it will become 'Loaded'
            while (tileEntry->getState() != TileState::Loaded)
                REPEAT_UNTIL(tileEntry->loadedCondition.wait(&tileEntry->loadedConditionLock));
        }

        if (!tileEntry->dataIsPresent)
        {
            // If there was no data, return same
            outTiledData.reset();
            return true;
        }
        else
        {
            // Otherwise, try to lock tile reference
            outTiledData = tileEntry->dataWeakRef.lock();

            // If successfully locked, just return it
            if (outTiledData)
                return true;

            // Otherwise consider this tile entry as expired, remove it from collection (it's safe to do that right now)
            // This will enable creation of new entry on next loop cycle
            _tileReferences.removeEntry(tileId, zoom);
            tileEntry.reset();
        }
    }

    const Stopwatch totalTimeStopwatch(
#if OSMAND_PERFORMANCE_METRICS
        true
#else
        metric != nullptr
#endif // OSMAND_PERFORMANCE_METRICS
        );

    // Obtain map objects data tile
    std::shared_ptr<IMapObjectsProvider::Data> dataTile;
    std::shared_ptr<Metric> submetric;
    owner->mapObjectsProvider->obtainData(
        tileId,
        zoom,
        dataTile,
        metric ? &submetric : nullptr,
        nullptr);
    if (metric && submetric)
        metric->addOrReplaceSubmetric(submetric);
    if (!dataTile)
    {
        // Store flag that there was no data and mark tile entry as 'Loaded'
        tileEntry->dataIsPresent = false;
        tileEntry->setState(TileState::Loaded);

        // Notify that tile has been loaded
        {
            QWriteLocker scopedLcoker(&tileEntry->loadedConditionLock);
            tileEntry->loadedCondition.wakeAll();
        }

        outTiledData.reset();
        return true;
    }

    // Get primitivised objects
    std::shared_ptr<MapPrimitiviser::PrimitivisedObjects> primitivisedObjects;
    if (owner->mode == MapPrimitivesProvider::Mode::AllObjectsWithoutPolygonFiltering)
    {
        primitivisedObjects = owner->primitiviser->primitiviseAllMapObjects(
            zoom,
            dataTile->mapObjects,
            //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance
            //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock
            nullptr, //_primitiviserCache,
            nullptr,
            metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseAllMapObjects>().get() : nullptr);
    }
    else if (owner->mode == MapPrimitivesProvider::Mode::AllObjectsWithPolygonFiltering)
    {
        primitivisedObjects = owner->primitiviser->primitiviseAllMapObjects(
            Utilities::getScaleDivisor31ToPixel(PointI(owner->tileSize, owner->tileSize), zoom),
            zoom,
            dataTile->mapObjects,
            //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance
            //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock
            nullptr, //_primitiviserCache,
            nullptr,
            metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseAllMapObjects>().get() : nullptr);
    }
    else if (owner->mode == MapPrimitivesProvider::Mode::WithoutSurface)
    {
        primitivisedObjects = owner->primitiviser->primitiviseWithoutSurface(
            Utilities::getScaleDivisor31ToPixel(PointI(owner->tileSize, owner->tileSize), zoom),
            zoom,
            dataTile->mapObjects,
            //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance
            //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock
            nullptr, //_primitiviserCache,
            nullptr,
            metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseWithoutSurface>().get() : nullptr);
    }
    else // if (owner->mode == MapPrimitivesProvider::Mode::WithSurface)
    {
        const auto tileBBox31 = Utilities::tileBoundingBox31(tileId, zoom);
        primitivisedObjects = owner->primitiviser->primitiviseWithSurface(
            tileBBox31,
            PointI(owner->tileSize, owner->tileSize),
            zoom,
            dataTile->tileSurfaceType,
            dataTile->mapObjects,
            //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance
            //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock
            nullptr, //_primitiviserCache,
            nullptr,
            metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseWithSurface>().get() : nullptr);
    }

    // Create tile
    const std::shared_ptr<MapPrimitivesProvider::Data> newTiledData(new MapPrimitivesProvider::Data(
        tileId,
        zoom,
        dataTile,
        primitivisedObjects,
        new RetainableCacheMetadata(tileEntry, dataTile->retainableCacheMetadata)));

    // Publish new tile
    outTiledData = newTiledData;

    // Store weak reference to new tile and mark it as 'Loaded'
    tileEntry->dataIsPresent = true;
    tileEntry->dataWeakRef = newTiledData;
    tileEntry->setState(TileState::Loaded);

    // Notify that tile has been loaded
    {
        QWriteLocker scopedLcoker(&tileEntry->loadedConditionLock);
        tileEntry->loadedCondition.wakeAll();
    }

    if (metric)
        metric->elapsedTime = totalStopwatch.elapsed();

#if OSMAND_PERFORMANCE_METRICS
#if OSMAND_PERFORMANCE_METRICS <= 1
    LogPrintf(LogSeverityLevel::Info,
        "%d polygons, %d polylines, %d points primitivised from %dx%d@%d in %fs",
        primitivisedObjects->polygons.size(),
        primitivisedObjects->polylines.size(),
        primitivisedObjects->polygons.size(),
        tileId.x,
        tileId.y,
        zoom,
        totalStopwatch.elapsed());
#else
    LogPrintf(LogSeverityLevel::Info,
        "%d polygons, %d polylines, %d points primitivised from %dx%d@%d in %fs:\n%s",
        primitivisedObjects->polygons.size(),
        primitivisedObjects->polylines.size(),
        primitivisedObjects->polygons.size(),
        tileId.x,
        tileId.y,
        zoom,
        totalStopwatch.elapsed(),
        qPrintable(metric ? metric->toString(QLatin1String("\t - ")) : QLatin1String("(null)")));
#endif // OSMAND_PERFORMANCE_METRICS <= 1
#endif // OSMAND_PERFORMANCE_METRICS
    
    return true;
}
예제 #22
0
// returns true if the double-click was handled and false if it wasn't
bool OnInverseSearch(WindowInfo *win, int x, int y)
{
    if (!HasPermission(Perm_DiskAccess) || gPluginMode) return false;
    if (!win->IsDocLoaded() || win->dm->engineType != Engine_PDF) return false;

    // Clear the last forward-search result
    win->fwdSearchMark.rects.Reset();
    InvalidateRect(win->hwndCanvas, NULL, FALSE);

    // On double-clicking error message will be shown to the user
    // if the PDF does not have a synchronization file
    if (!win->pdfsync) {
        int err = Synchronizer::Create(win->loadedFilePath,
            static_cast<PdfEngine *>(win->dm->engine), &win->pdfsync);
        if (err == PDFSYNCERR_SYNCFILE_NOTFOUND) {
            // We used to warn that "No synchronization file found" at this
            // point if gGlobalPrefs->enableTeXEnhancements is set; we no longer
            // so do because a double-click has several other meanings
            // (selecting a word or an image, navigating quickly using links)
            // and showing an unrelated warning in all those cases seems wrong
            return false;
        }
        if (err != PDFSYNCERR_SUCCESS) {
            ShowNotification(win, _TR("Synchronization file cannot be opened"));
            return true;
        }
        gGlobalPrefs->enableTeXEnhancements = true;
    }

    int pageNo = win->dm->GetPageNoByPoint(PointI(x, y));
    if (!win->dm->ValidPageNo(pageNo))
        return false;

    PointI pt = win->dm->CvtFromScreen(PointI(x, y), pageNo).Convert<int>();
    ScopedMem<WCHAR> srcfilepath;
    UINT line, col;
    int err = win->pdfsync->DocToSource(pageNo, pt, srcfilepath, &line, &col);
    if (err != PDFSYNCERR_SUCCESS) {
        ShowNotification(win, _TR("No synchronization info at this position"));
        return true;
    }

    WCHAR *inverseSearch = gGlobalPrefs->inverseSearchCmdLine;
    if (!inverseSearch)
        // Detect a text editor and use it as the default inverse search handler for now
        inverseSearch = AutoDetectInverseSearchCommands();

    ScopedMem<WCHAR> cmdline;
    if (inverseSearch)
        cmdline.Set(win->pdfsync->PrepareCommandline(inverseSearch, srcfilepath, line, col));
    if (!str::IsEmpty(cmdline.Get())) {
        // resolve relative paths with relation to SumatraPDF.exe's directory
        ScopedMem<WCHAR> appDir(GetExePath());
        if (appDir)
            appDir.Set(path::GetDir(appDir));
        ScopedHandle process(LaunchProcess(cmdline, appDir));
        if (!process)
            ShowNotification(win, _TR("Cannot start inverse search command. Please check the command line in the settings."));
    }
    else if (gGlobalPrefs->enableTeXEnhancements)
        ShowNotification(win, _TR("Cannot start inverse search command. Please check the command line in the settings."));

    if (inverseSearch != gGlobalPrefs->inverseSearchCmdLine)
        free(inverseSearch);

    return true;
}
예제 #23
0
static void GeomTest()
{
    PointD ptD(12.4, -13.6);
    utassert(ptD.x == 12.4 && ptD.y == -13.6);
    PointI ptI = ptD.ToInt();
    utassert(ptI.x == 12 && ptI.y == -14);
    ptD = ptI.Convert<double>();
    utassert(PointD(12, -14) == ptD);
    utassert(PointD(12.4, -13.6) != ptD);

    SizeD szD(7.7, -3.3);
    utassert(szD.dx == 7.7 && szD.dy == -3.3);
    SizeI szI = szD.ToInt();
    utassert(szI.dx == 8 && szI.dy == -3);
    szD = szI.Convert<double>();
    utassert(SizeD(8, -3) == szD);

    utassert(!szD.IsEmpty() && !szI.IsEmpty());
    utassert(SizeI().IsEmpty() && SizeD().IsEmpty());

    struct SRIData {
        int     x1s, x1e, y1s, y1e;
        int     x2s, x2e, y2s, y2e;
        bool    intersect;
        int     i_xs, i_xe, i_ys, i_ye;
        int     u_xs, u_xe, u_ys, u_ye;
    } testData[] = {
        { 0,10, 0,10,   0,10, 0,10,  true,  0,10, 0,10,  0,10, 0,10 }, /* complete intersect */
        { 0,10, 0,10,  20,30,20,30,  false, 0, 0, 0, 0,  0,30, 0,30 }, /* no intersect */
        { 0,10, 0,10,   5,15, 0,10,  true,  5,10, 0,10,  0,15, 0,10 }, /* { | } | */
        { 0,10, 0,10,   5, 7, 0,10,  true,  5, 7, 0,10,  0,10, 0,10 }, /* { | | } */
        { 0,10, 0,10,   5, 7, 5, 7,  true,  5, 7, 5, 7,  0,10, 0,10 },
        { 0,10, 0,10,   5, 15,5,15,  true,  5,10, 5,10,  0,15, 0,15 },
    };

    for (size_t i = 0; i < dimof(testData); i++) {
        struct SRIData *curr = &testData[i];

        RectI rx1(curr->x1s, curr->y1s, curr->x1e - curr->x1s, curr->y1e - curr->y1s);
        RectI rx2 = RectI::FromXY(curr->x2s, curr->y2s, curr->x2e, curr->y2e);
        RectI isect = rx1.Intersect(rx2);
        if (curr->intersect) {
            utassert(!isect.IsEmpty());
            utassert(isect.x == curr->i_xs && isect.y == curr->i_ys);
            utassert(isect.x + isect.dx == curr->i_xe && isect.y + isect.dy == curr->i_ye);
        }
        else {
            utassert(isect.IsEmpty());
        }
        RectI urect = rx1.Union(rx2);
        utassert(urect.x == curr->u_xs && urect.y == curr->u_ys);
        utassert(urect.x + urect.dx == curr->u_xe && urect.y + urect.dy == curr->u_ye);

        /* if we swap rectangles, the results should be the same */
        std::swap(rx1, rx2);
        isect = rx1.Intersect(rx2);
        if (curr->intersect) {
            utassert(!isect.IsEmpty());
            utassert(isect.x == curr->i_xs && isect.y == curr->i_ys);
            utassert(isect.x + isect.dx == curr->i_xe && isect.y + isect.dy == curr->i_ye);
        }
        else {
            utassert(isect.IsEmpty());
        }
        urect = rx1.Union(rx2);
        utassert(RectI::FromXY(curr->u_xs, curr->u_ys, curr->u_xe, curr->u_ye) == urect);

        utassert(!rx1.Contains(PointI(-2, -2)));
        utassert(rx1.Contains(rx1.TL()));
        utassert(!rx1.Contains(PointI(rx1.x, INT_MAX)));
        utassert(!rx1.Contains(PointI(INT_MIN, rx1.y)));
    }
}
예제 #24
0
void OsmAnd::Utilities::scanlineFillPolygon(const unsigned int verticesCount, const PointF* vertices, std::function<void(const PointI&)> fillPoint)
{
    // Find min-max of Y
    float yMinF, yMaxF;
    yMinF = yMaxF = vertices[0].y;
    for(auto idx = 1u; idx < verticesCount; idx++)
    {
        const auto& y = vertices[idx].y;
        if (y > yMaxF)
            yMaxF = y;
        if (y < yMinF)
            yMinF = y;
    }
    const auto rowMin = qFloor(yMinF);
    const auto rowMax = qFloor(yMaxF);

    // Build set of edges
    struct Edge
    {
        const PointF* v0;
        int startRow;
        const PointF* v1;
        int endRow;
        float xOrigin;
        float slope;
        int nextRow;
    };
    QVector<Edge*> edges;
    edges.reserve(verticesCount);
    auto edgeIdx = 0u;
    for(auto idx = 0u, prevIdx = verticesCount - 1; idx < verticesCount; prevIdx = idx++)
    {
        auto v0 = &vertices[prevIdx];
        auto v1 = &vertices[idx];

        if (v0->y == v1->y)
        {
            // Horizontal edge
            auto edge = new Edge();
            edge->v0 = v0;
            edge->v1 = v1;
            edge->startRow = qFloor(edge->v0->y);
            edge->endRow = qFloor(edge->v1->y);
            edge->xOrigin = edge->v0->x;
            edge->slope = 0;
            edge->nextRow = qFloor(edge->v0->y) + 1;
            edges.push_back(edge);
            //LogPrintf(LogSeverityLevel::Debug, "Edge %p y(%d %d)(%f %f), next row = %d", edge, edge->startRow, edge->endRow, edge->v0->y, edge->v1->y, edge->nextRow);

            continue;
        }

        const PointF* pLower = nullptr;
        const PointF* pUpper = nullptr;
        if (v0->y < v1->y)
        {
            // Up-going edge
            pLower = v0;
            pUpper = v1;
        }
        else if (v0->y > v1->y)
        {
            // Down-going edge
            pLower = v1;
            pUpper = v0;
        }

        // Fill edge 
        auto edge = new Edge();
        edge->v0 = pLower;
        edge->v1 = pUpper;
        edge->startRow = qFloor(edge->v0->y);
        edge->endRow = qFloor(edge->v1->y);
        edge->slope = (edge->v1->x - edge->v0->x) / (edge->v1->y - edge->v0->y);
        edge->xOrigin = edge->v0->x - edge->slope * (edge->v0->y - qFloor(edge->v0->y));
        edge->nextRow = qFloor(edge->v1->y) + 1;
        for(auto vertexIdx = 0u; vertexIdx < verticesCount; vertexIdx++)
        {
            const auto& v = vertices[vertexIdx];

            if (v.y > edge->v0->y && qFloor(v.y) < edge->nextRow)
                edge->nextRow = qFloor(v.y);
        }
        //LogPrintf(LogSeverityLevel::Debug, "Edge %p y(%d %d)(%f %f), next row = %d", edge, edge->startRow, edge->endRow, edge->v0->y, edge->v1->y, edge->nextRow);
        edges.push_back(edge);
    }

    // Sort edges by ascending Y
    qSort(edges.begin(), edges.end(), [](Edge* l, Edge* r) -> bool
    {
        return l->v0->y > r->v0->y;
    });

    // Loop in [yMin .. yMax]
    QVector<Edge*> aet;
    aet.reserve(edges.size());
    for(auto rowIdx = rowMin; rowIdx <= rowMax;)
    {
        //LogPrintf(LogSeverityLevel::Debug, "------------------ %d -----------------", rowIdx);

        // Find active edges
        int nextRow = rowMax;
        for(const auto& edge : constOf(edges))
        {
            const auto isHorizontal = (edge->startRow == edge->endRow);
            if (nextRow > edge->nextRow && edge->nextRow > rowIdx && !isHorizontal)
                nextRow = edge->nextRow;

            if (edge->startRow != rowIdx)
                continue;

            if (isHorizontal)
            {
                // Fill horizontal edge
                const auto xMin = qFloor(qMin(edge->v0->x, edge->v1->x));
                const auto xMax = qFloor(qMax(edge->v0->x, edge->v1->x));
                /*for(auto x = xMin; x <= xMax; x++)
                    fillPoint(PointI(x, rowIdx));*/
                continue;
            }

            //LogPrintf(LogSeverityLevel::Debug, "line %d. Adding edge %p y(%f %f)", rowIdx, edge, edge->v0->y, edge->v1->y);
            aet.push_back(edge);
            continue;
        }

        // If there are no active edges, we've finished filling
        if (aet.isEmpty())
            break;
        assert(aet.size() % 2 == 0);

        // Sort aet by X
        qSort(aet.begin(), aet.end(), [](Edge* l, Edge* r) -> bool
        {
            return l->v0->x > r->v0->x;
        });

        // Find next row
        for(; rowIdx < nextRow; rowIdx++)
        {
            const unsigned int pairsCount = aet.size() / 2;

            auto itEdgeL = aet.cbegin();
            auto itEdgeR = itEdgeL + 1;

            for(auto pairIdx = 0u; pairIdx < pairsCount; pairIdx++, itEdgeL = ++itEdgeR, ++itEdgeR)
            {
                auto lEdge = *itEdgeL;
                auto rEdge = *itEdgeR;

                // Fill from l to r
                auto lXf = lEdge->xOrigin + (rowIdx - lEdge->startRow + 0.5f) * lEdge->slope;
                auto rXf = rEdge->xOrigin + (rowIdx - rEdge->startRow + 0.5f) * rEdge->slope;
                auto xMinF = qMin(lXf, rXf);
                auto xMaxF = qMax(lXf, rXf);
                auto xMin = qFloor(xMinF);
                auto xMax = qFloor(xMaxF);

                LogPrintf(LogSeverityLevel::Debug, "line %d from %d(%f) to %d(%f)", rowIdx, xMin, xMinF, xMax, xMaxF);
                /*for(auto x = xMin; x <= xMax; x++)
                    fillPoint(PointI(x, rowIdx));*/
            }
        }

        // Deactivate those edges that have end at yNext
        for(auto itEdge = aet.begin(); itEdge != aet.end();)
        {
            auto edge = *itEdge;

            if (edge->endRow <= nextRow)
            {
                // When we're done processing the edge, fill it Y-by-X
                auto startCol = qFloor(edge->v0->x);
                auto endCol = qFloor(edge->v1->x);
                auto revSlope = 1.0f / edge->slope;
                auto yOrigin = edge->v0->y - revSlope * (edge->v0->x - qFloor(edge->v0->x));
                auto xMax = qMax(startCol, endCol);
                auto xMin = qMin(startCol, endCol);
                for(auto colIdx = xMin; colIdx <= xMax; colIdx++)
                {
                    auto yf = yOrigin + (colIdx - startCol + 0.5f) * revSlope;
                    auto y = qFloor(yf);

                    LogPrintf(LogSeverityLevel::Debug, "col %d(s%d) added Y = %d (%f)", colIdx, colIdx - startCol, y, yf);
                    fillPoint(PointI(colIdx, y));
                }

                //////////////////////////////////////////////////////////////////////////
                auto yMax = qMax(edge->startRow, edge->endRow);
                auto yMin = qMin(edge->startRow, edge->endRow);
                for(auto rowIdx_ = yMin; rowIdx_ <= yMax; rowIdx_++)
                {
                    auto xf = edge->xOrigin + (rowIdx_ - edge->startRow + 0.5f) * edge->slope;
                    auto x = qFloor(xf);

                    LogPrintf(LogSeverityLevel::Debug, "row %d(s%d) added Y = %d (%f)", rowIdx_, rowIdx_ - edge->startRow, x, xf);
                    fillPoint(PointI(x, rowIdx_));
                }
                //////////////////////////////////////////////////////////////////////////

                //LogPrintf(LogSeverityLevel::Debug, "line %d. Removing edge %p y(%f %f)", rowIdx, edge, edge->v0->y, edge->v1->y);
                itEdge = aet.erase(itEdge);
            }
            else
                ++itEdge;
        }
    }

    // Cleanup
    for(const auto& edge : constOf(edges))
        delete edge;
}
예제 #25
0
LRESULT CALLBACK NotificationWnd::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\
{
    NotificationWnd *wnd = (NotificationWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (WM_ERASEBKGND == message) {
        // do nothing, helps to avoid flicker
        return TRUE;
    }
    if (WM_TIMER == message && TIMEOUT_TIMER_ID == wParam) {
        if (wnd->notificationCb)
            wnd->notificationCb->RemoveNotification(wnd);
        else
            delete wnd;
        return 0;
    }
    if (WM_PAINT == message && wnd) {
        PAINTSTRUCT ps;
        HDC hdcWnd = BeginPaint(hwnd, &ps);

        ClientRect rect(hwnd);
        DoubleBuffer buffer(hwnd, rect);
        HDC hdc = buffer.GetDC();
        HFONT oldfnt = SelectFont(hdc, wnd->font);

        RECT rTmp = rect.ToRECT();
        DrawFrameControl(hdc, &rTmp, DFC_BUTTON, DFCS_BUTTONPUSH);
        if (wnd->highlight) {
            SetBkMode(hdc, OPAQUE);
            SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
        }
        else {
            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
        }

        rect.Inflate(-PADDING, -PADDING);
        RectI rectMsg = rect;
        if (wnd->hasProgress)
            rectMsg.dy -= PROGRESS_HEIGHT + PADDING / 2;
        if (wnd->hasCancel)
            rectMsg.dx -= 20;
        ScopedMem<WCHAR> text(win::GetText(hwnd));
        rTmp = rectMsg.ToRECT();
        DrawText(hdc, text, -1, &rTmp, DT_SINGLELINE | DT_NOPREFIX);

        if (wnd->hasCancel) {
            rTmp = GetCancelRect(hwnd).ToRECT();
            DrawFrameControl(hdc, &rTmp, DFC_CAPTION, DFCS_CAPTIONCLOSE | DFCS_FLAT);
        }

        if (wnd->hasProgress) {
            rect.dx = wnd->progressWidth;
            rect.y += rectMsg.dy + PADDING / 2;
            rect.dy = PROGRESS_HEIGHT;
            PaintRect(hdc, rect);

            rect.x += 2;
            rect.dx = (wnd->progressWidth - 3) * wnd->progress / 100;
            rect.y += 2;
            rect.dy -= 3;

            HBRUSH brush = GetStockBrush(BLACK_BRUSH);
            rTmp = rect.ToRECT();
            FillRect(hdc, &rTmp, brush);
            DeleteObject(brush);
        }

        SelectFont(hdc, oldfnt);

        buffer.Flush(hdcWnd);
        EndPaint(hwnd, &ps);
        return 0;
    }
    if (WM_SETCURSOR == message && wnd->hasCancel) {
        POINT pt;
        if (GetCursorPosInHwnd(hwnd, pt) &&
            GetCancelRect(hwnd).Contains(PointI(pt.x, pt.y))) {
            SetCursor(LoadCursor(NULL, IDC_HAND));
            return TRUE;
        }
    }
    if (WM_LBUTTONUP == message && wnd->hasCancel) {
        if (GetCancelRect(hwnd).Contains(PointI(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))) {
            if (wnd->notificationCb)
                wnd->notificationCb->RemoveNotification(wnd);
            else
                delete wnd;
            return 0;
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
예제 #26
0
파일: Camera.cpp 프로젝트: brunoweig/mmx
 *      Author: bruno
 */

#include "Camera.hpp"
#include <sstream>

Box2D Camera::geometry = Box2D(0, 0, 640, 480);
PointF Camera::lockedPosition = PointF(0, 0);
bool Camera::fullScreen = true;
bool Camera::scrollingX = false;
bool Camera::scrollingY = false;
bool Camera::lockedX = false;
bool Camera::lockedY = false;
bool Camera::lockedRange = false;
bool Camera::translating = false;
PointI Camera::beginRangeLock = PointI(0, 0);
PointI Camera::endRangeLock = PointI(0, 0);

Camera::Camera() {

}

Camera::~Camera() {
}

PointF Camera::getPosition() {
    return PointF(geometry.getLeftSide(), geometry.getTopSide());
}

PointI Camera::getResolution() {
    return PointI(geometry.getWidth(), geometry.getHeight());
예제 #27
0
void OsmAnd::SymbolRasterizer_P::rasterize(
    const std::shared_ptr<const MapPrimitiviser::PrimitivisedObjects>& primitivisedObjects,
    QList< std::shared_ptr<const RasterizedSymbolsGroup> >& outSymbolsGroups,
    const FilterByMapObject filter,
    const std::shared_ptr<const IQueryController>& queryController) const
{
    const auto& env = primitivisedObjects->mapPresentationEnvironment;

    for (const auto& symbolGroupEntry : rangeOf(constOf(primitivisedObjects->symbolsGroups)))
    {
        if (queryController && queryController->isAborted())
            return;

        const auto& mapObject = symbolGroupEntry.key();
        const auto& symbolsGroup = symbolGroupEntry.value();

        //////////////////////////////////////////////////////////////////////////
        //if (mapObject->toString().contains("1333827773"))
        //{
        //    int i = 5;
        //}
        //////////////////////////////////////////////////////////////////////////

        // Apply filter, if it's present
        if (filter && !filter(mapObject))
            continue;

        // Create group
        const std::shared_ptr<RasterizedSymbolsGroup> group(new RasterizedSymbolsGroup(
            mapObject));

        // Total offset allows several symbols to stack into column. Offset specifies center of symbol bitmap.
        // This offset is computed only in case symbol is not on-path and not along-path
        PointI totalOffset;

        for (const auto& symbol : constOf(symbolsGroup->symbols))
        {
            if (queryController && queryController->isAborted())
                return;

            if (const auto& textSymbol = std::dynamic_pointer_cast<const MapPrimitiviser::TextSymbol>(symbol))
            {
                TextRasterizer::Style style;
                if (!textSymbol->drawOnPath && textSymbol->shieldResourceName.isEmpty())
                    style.wrapWidth = textSymbol->wrapWidth;

                QList< std::shared_ptr<const SkBitmap> > backgroundLayers;
                if (!textSymbol->shieldResourceName.isEmpty())
                {
                    std::shared_ptr<const SkBitmap> shield;
                    env->obtainTextShield(textSymbol->shieldResourceName, shield);

                    if (shield)
                        backgroundLayers.push_back(shield);
                }
                if (!textSymbol->underlayIconResourceName.isEmpty())
                {
                    std::shared_ptr<const SkBitmap> icon;
                    env->obtainMapIcon(textSymbol->underlayIconResourceName, icon);
                    if (icon)
                        backgroundLayers.push_back(icon);
                }

                style.backgroundBitmap = SkiaUtilities::mergeBitmaps(backgroundLayers);
                if (!qFuzzyCompare(textSymbol->scaleFactor, 1.0f) && style.backgroundBitmap)
                {
                    style.backgroundBitmap = SkiaUtilities::scaleBitmap(
                        style.backgroundBitmap,
                        textSymbol->scaleFactor,
                        textSymbol->scaleFactor);
                }

                style
                    .setBold(textSymbol->isBold)
                    .setItalic(textSymbol->isItalic)
                    .setColor(textSymbol->color)
                    .setSize(static_cast<int>(textSymbol->size));

                if (textSymbol->shadowRadius > 0)
                {
                    style
                        .setHaloColor(textSymbol->shadowColor)
                        .setHaloRadius(textSymbol->shadowRadius);
                }

                float lineSpacing;
                float symbolExtraTopSpace;
                float symbolExtraBottomSpace;
                QVector<SkScalar> glyphsWidth;
                const auto rasterizedText = owner->textRasterizer->rasterize(
                    textSymbol->value,
                    style,
                    textSymbol->drawOnPath ? &glyphsWidth : nullptr,
                    &symbolExtraTopSpace,
                    &symbolExtraBottomSpace,
                    &lineSpacing);
                if (!rasterizedText)
                    continue;

#if OSMAND_DUMP_SYMBOLS
                {
                    QDir::current().mkpath("text_symbols");
                    std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder());
                    QString filename;
                    filename.sprintf("%s\\text_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedText.get());
                    encoder->encodeFile(qPrintable(filename), *rasterizedText.get(), 100);
                }
#endif // OSMAND_DUMP_SYMBOLS

                if (textSymbol->drawOnPath)
                {
                    // Publish new rasterized symbol
                    const std::shared_ptr<RasterizedOnPathSymbol> rasterizedSymbol(new RasterizedOnPathSymbol(
                        group,
                        textSymbol));
                    rasterizedSymbol->bitmap = qMove(rasterizedText);
                    rasterizedSymbol->order = textSymbol->order;
                    rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Text;
                    rasterizedSymbol->content = textSymbol->value;
                    rasterizedSymbol->languageId = textSymbol->languageId;
                    rasterizedSymbol->minDistance = textSymbol->minDistance;
                    rasterizedSymbol->glyphsWidth = glyphsWidth;
                    group->symbols.push_back(qMove(rasterizedSymbol));
                }
                else
                {
                    // Calculate local offset. Since offset specifies center, it's a sum of
                    //  - vertical offset
                    //  - extra top space (which should be in texture, but not rendered, since transparent)
                    //  - height / 2
                    // This calculation is used only if this symbol is not first. Otherwise only following is used:
                    //  - vertical offset
                    PointI localOffset;
                    localOffset.y += textSymbol->verticalOffset;
                    if (!group->symbols.isEmpty() && !textSymbol->drawAlongPath)
                    {
                        localOffset.y += symbolExtraTopSpace;
                        localOffset.y += rasterizedText->height() / 2;
                    }

                    // Increment total offset
                    if (!textSymbol->drawAlongPath)
                        totalOffset += localOffset;

                    // Publish new rasterized symbol
                    const std::shared_ptr<RasterizedSpriteSymbol> rasterizedSymbol(new RasterizedSpriteSymbol(group, textSymbol));
                    rasterizedSymbol->bitmap = rasterizedText;
                    rasterizedSymbol->order = textSymbol->order;
                    rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Text;
                    rasterizedSymbol->content = textSymbol->value;
                    rasterizedSymbol->languageId = textSymbol->languageId;
                    rasterizedSymbol->minDistance = textSymbol->minDistance;
                    rasterizedSymbol->location31 = textSymbol->location31;
                    rasterizedSymbol->offset = textSymbol->drawAlongPath ? localOffset : totalOffset;
                    rasterizedSymbol->drawAlongPath = textSymbol->drawAlongPath;
                    if (!qIsNaN(textSymbol->intersectionSizeFactor))
                    {
                        rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                            static_cast<int>(textSymbol->intersectionSizeFactor * rasterizedText->width()),
                            static_cast<int>(textSymbol->intersectionSizeFactor * rasterizedText->height())));
                    }
                    else if (!qIsNaN(textSymbol->intersectionSize))
                    {
                        rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                            static_cast<int>(textSymbol->intersectionSize),
                            static_cast<int>(textSymbol->intersectionSize)));
                    }
                    else if (!qIsNaN(textSymbol->intersectionMargin))
                    {
                        rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                            rasterizedText->width() + static_cast<int>(textSymbol->intersectionMargin),
                            rasterizedText->height() + static_cast<int>(textSymbol->intersectionMargin)));
                    }
                    else
                    {
                        rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                            static_cast<int>(rasterizedText->width()),
                            static_cast<int>(rasterizedText->height())));

                        rasterizedSymbol->intersectionBBox.top() -= static_cast<int>(symbolExtraTopSpace);
                        rasterizedSymbol->intersectionBBox.bottom() += static_cast<int>(symbolExtraBottomSpace);
                    }
                    group->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol)));

                    // Next symbol should also take into account:
                    //  - height / 2
                    //  - extra bottom space (which should be in texture, but not rendered, since transparent)
                    //  - spacing between lines
                    if (!textSymbol->drawAlongPath)
                    {
                        totalOffset.y += rasterizedText->height() / 2;
                        totalOffset.y += symbolExtraBottomSpace;
                        totalOffset.y += qCeil(lineSpacing);
                    }
                }
            }
            else if (const auto& iconSymbol = std::dynamic_pointer_cast<const MapPrimitiviser::IconSymbol>(symbol))
            {
                std::shared_ptr<const SkBitmap> iconBitmap;
                if (!env->obtainMapIcon(iconSymbol->resourceName, iconBitmap) || !iconBitmap)
                    continue;
                if (!qFuzzyCompare(iconSymbol->scaleFactor, 1.0f))
                {
                    iconBitmap = SkiaUtilities::scaleBitmap(
                        iconBitmap,
                        iconSymbol->scaleFactor,
                        iconSymbol->scaleFactor);
                }

                std::shared_ptr<const SkBitmap> backgroundBitmap;
                if (!iconSymbol->shieldResourceName.isEmpty())
                {
                    env->obtainIconShield(iconSymbol->shieldResourceName, backgroundBitmap);

                    if (!qFuzzyCompare(iconSymbol->scaleFactor, 1.0f) && backgroundBitmap)
                    {
                        backgroundBitmap = SkiaUtilities::scaleBitmap(
                            backgroundBitmap,
                            iconSymbol->scaleFactor,
                            iconSymbol->scaleFactor);
                    }
                }

                QList< std::shared_ptr<const SkBitmap> > layers;
                if (backgroundBitmap)
                    layers.push_back(backgroundBitmap);
                for (const auto& overlayResourceName : constOf(iconSymbol->underlayResourceNames))
                {
                    std::shared_ptr<const SkBitmap> underlayBitmap;
                    if (!env->obtainMapIcon(overlayResourceName, underlayBitmap) || !underlayBitmap)
                        continue;

                    layers.push_back(underlayBitmap);
                }
                layers.push_back(iconBitmap);
                for (const auto& overlayResourceName : constOf(iconSymbol->overlayResourceNames))
                {
                    std::shared_ptr<const SkBitmap> overlayBitmap;
                    if (!env->obtainMapIcon(overlayResourceName, overlayBitmap) || !overlayBitmap)
                        continue;

                    layers.push_back(overlayBitmap);
                }

                // Compose final image
                const auto rasterizedIcon = SkiaUtilities::mergeBitmaps(layers);

#if OSMAND_DUMP_SYMBOLS
                {
                    QDir::current().mkpath("icon_symbols");
                    std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder());
                    QString filename;
                    filename.sprintf("%s\\icon_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedIcon.get());
                    encoder->encodeFile(qPrintable(filename), *rasterizedIcon, 100);
                }
#endif // OSMAND_DUMP_SYMBOLS

                // Calculate local offset. Since offset specifies center, it's a sum of
                //  - height / 2
                // This calculation is used only if this symbol is not first. Otherwise nothing is used.
                PointI localOffset;
                if (!qFuzzyIsNull(iconSymbol->offsetFactor.x))
                    localOffset.x = qRound(iconSymbol->offsetFactor.x * rasterizedIcon->width());
                if (!qFuzzyIsNull(iconSymbol->offsetFactor.y))
                    localOffset.y = qRound(iconSymbol->offsetFactor.y * rasterizedIcon->height());
                if (!group->symbols.isEmpty() && !iconSymbol->drawAlongPath)
                    localOffset.y += rasterizedIcon->height() / 2;

                // Increment total offset
                if (!iconSymbol->drawAlongPath)
                    totalOffset += localOffset;

                // Publish new rasterized symbol
                const std::shared_ptr<RasterizedSpriteSymbol> rasterizedSymbol(new RasterizedSpriteSymbol(group, iconSymbol));
                rasterizedSymbol->bitmap = rasterizedIcon;
                rasterizedSymbol->order = iconSymbol->order;
                rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Icon;
                rasterizedSymbol->content = iconSymbol->resourceName;
                rasterizedSymbol->languageId = LanguageId::Invariant;
                rasterizedSymbol->minDistance = iconSymbol->minDistance;
                rasterizedSymbol->location31 = iconSymbol->location31;
                rasterizedSymbol->offset = iconSymbol->drawAlongPath ? localOffset : totalOffset;
                rasterizedSymbol->drawAlongPath = iconSymbol->drawAlongPath;
                if (!qIsNaN(iconSymbol->intersectionSizeFactor))
                {
                    rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                        static_cast<int>(iconSymbol->intersectionSizeFactor * rasterizedIcon->width()),
                        static_cast<int>(iconSymbol->intersectionSizeFactor * rasterizedIcon->height())));
                }
                else if (!qIsNaN(iconSymbol->intersectionSize))
                {
                    rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                        static_cast<int>(iconSymbol->intersectionSize),
                        static_cast<int>(iconSymbol->intersectionSize)));
                }
                else if (!qIsNaN(iconSymbol->intersectionMargin))
                {
                    rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                        rasterizedIcon->width() + static_cast<int>(iconSymbol->intersectionMargin),
                        rasterizedIcon->height() + static_cast<int>(iconSymbol->intersectionMargin)));
                }
                else
                {
                    rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI(
                        static_cast<int>(rasterizedIcon->width()),
                        static_cast<int>(rasterizedIcon->height())));
                }
                group->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol)));

                // Next symbol should also take into account:
                //  - height / 2
                if (!iconSymbol->drawAlongPath)
                    totalOffset.y += rasterizedIcon->height() / 2;
            }
        }

        // Add group to output
        outSymbolsGroups.push_back(qMove(group));
    }
}
예제 #28
0
파일: Camera.cpp 프로젝트: brunoweig/mmx
PointI Camera::getResolution() {
    return PointI(geometry.getWidth(), geometry.getHeight());
}
예제 #29
0
std::shared_ptr<OsmAnd::MapSymbolsGroup> OsmAnd::MapMarker_P::inflateSymbolsGroup() const
{
    QReadLocker scopedLocker(&_lock);

    bool ok;

    // Construct new map symbols group for this marker
    const std::shared_ptr<MapSymbolsGroup> symbolsGroup(new LinkedMapSymbolsGroup(
        std::const_pointer_cast<MapMarker_P>(shared_from_this())));
    symbolsGroup->presentationMode |= MapSymbolsGroup::PresentationModeFlag::ShowAllOrNothing;

    int order = owner->baseOrder;

    if (owner->isAccuracyCircleSupported)
    {
        // Add a circle that represent precision circle
        const std::shared_ptr<AccuracyCircleMapSymbol> accuracyCircleSymbol(new AccuracyCircleMapSymbol(
            symbolsGroup));
        accuracyCircleSymbol->order = order++;
        accuracyCircleSymbol->position31 = _position;
        VectorMapSymbol::generateCirclePrimitive(*accuracyCircleSymbol, owner->accuracyCircleBaseColor.withAlpha(0.25f));
        accuracyCircleSymbol->isHidden = _isHidden && !_isAccuracyCircleVisible;
        accuracyCircleSymbol->scale = _accuracyCircleRadius;
        accuracyCircleSymbol->scaleType = VectorMapSymbol::ScaleType::InMeters;
        accuracyCircleSymbol->direction = Q_SNAN;
        symbolsGroup->symbols.push_back(accuracyCircleSymbol);

        // Add a ring-line that represent precision circle
        const std::shared_ptr<AccuracyCircleMapSymbol> precisionRingSymbol(new AccuracyCircleMapSymbol(
            symbolsGroup));
        precisionRingSymbol->order = order++;
        precisionRingSymbol->position31 = _position;
        VectorMapSymbol::generateRingLinePrimitive(*precisionRingSymbol, owner->accuracyCircleBaseColor.withAlpha(0.4f));
        precisionRingSymbol->isHidden = _isHidden && !_isAccuracyCircleVisible;
        precisionRingSymbol->scale = _accuracyCircleRadius;
        precisionRingSymbol->scaleType = VectorMapSymbol::ScaleType::InMeters;
        precisionRingSymbol->direction = Q_SNAN;
        symbolsGroup->symbols.push_back(precisionRingSymbol);
    }

    // Set of OnSurfaceMapSymbol from onMapSurfaceIcons
    for (const auto& itOnMapSurfaceIcon : rangeOf(constOf(owner->onMapSurfaceIcons)))
    {
        const auto key = itOnMapSurfaceIcon.key();
        const auto& onMapSurfaceIcon = itOnMapSurfaceIcon.value();

        std::shared_ptr<SkBitmap> iconClone(new SkBitmap());
        ok = onMapSurfaceIcon->deepCopyTo(iconClone.get());
        assert(ok);

        // Get direction
        float direction = 0.0f;
        const auto citDirection = _directions.constFind(key);
        if (citDirection != _directions.cend())
            direction = *citDirection;

        const std::shared_ptr<KeyedOnSurfaceRasterMapSymbol> onMapSurfaceIconSymbol(new KeyedOnSurfaceRasterMapSymbol(
            key,
            symbolsGroup));
        onMapSurfaceIconSymbol->order = order++;
        onMapSurfaceIconSymbol->bitmap = iconClone;
        onMapSurfaceIconSymbol->size = PointI(iconClone->width(), iconClone->height());
        onMapSurfaceIconSymbol->content = QString().sprintf("markerGroup(%p:%p)->onMapSurfaceIconBitmap:%p", this, symbolsGroup.get(), iconClone->getPixels());
        onMapSurfaceIconSymbol->languageId = LanguageId::Invariant;
        onMapSurfaceIconSymbol->position31 = _position;
        onMapSurfaceIconSymbol->direction = direction;
        onMapSurfaceIconSymbol->isHidden = _isHidden;
        symbolsGroup->symbols.push_back(onMapSurfaceIconSymbol);
    }

    // SpriteMapSymbol with pinIconBitmap as an icon
    if (owner->pinIcon)
    {
        std::shared_ptr<SkBitmap> pinIcon(new SkBitmap());
        ok = owner->pinIcon->deepCopyTo(pinIcon.get());
        assert(ok);

        const std::shared_ptr<BillboardRasterMapSymbol> pinIconSymbol(new BillboardRasterMapSymbol(
            symbolsGroup));
        pinIconSymbol->order = order++;
        pinIconSymbol->bitmap = pinIcon;
        pinIconSymbol->size = PointI(pinIcon->width(), pinIcon->height());
        pinIconSymbol->content = QString().sprintf("markerGroup(%p:%p)->pinIconBitmap:%p", this, symbolsGroup.get(), pinIcon->getPixels());
        pinIconSymbol->languageId = LanguageId::Invariant;
        pinIconSymbol->position31 = _position;
        pinIconSymbol->offset = PointI(0, -pinIcon->height() / 2);
        pinIconSymbol->isHidden = _isHidden;
        pinIconSymbol->modulationColor = _pinIconModulationColor;
        symbolsGroup->symbols.push_back(pinIconSymbol);
    }

    return symbolsGroup;
}
예제 #30
0
err_t JpegDecoder::readImage(Image& image)
{
  JpegLibrary& jpeg = reinterpret_cast<JpegCodecProvider*>(_provider)->_jpegLibrary;
  FOG_ASSERT(jpeg.err == ERR_OK);

  err_t err = ERR_OK;

  struct jpeg_decompress_struct cinfo;
  MyJpegSourceMgr srcmgr;
  MyJpegErrorMgr jerr;
  JSAMPROW rowptr[1];

  uint32_t format = IMAGE_FORMAT_RGB24;
  int bpp = 3;

  // Create a decompression structure and load the header.
  cinfo.err = jpeg.std_error(&jerr.errmgr);
  jerr.errmgr.error_exit = MyJpegErrorExit;
  jerr.errmgr.output_message = MyJpegMessage;

  if (setjmp(jerr.escape))
  {
    // Error condition.
    jpeg.destroy_decompress(&cinfo);
    return ERR_IMAGE_LIBJPEG_ERROR;
  }

  jpeg.create_decompress(&cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_decompress_struct));

  cinfo.src = (struct jpeg_source_mgr *)&srcmgr;
  srcmgr.pub.init_source = MyJpegInitSource;
  srcmgr.pub.fill_input_buffer = MyJpegFillInputBuffer;
  srcmgr.pub.skip_input_data = MyJpegSkipInputData;
  srcmgr.pub.resync_to_restart = jpeg.resync_to_restart;
  srcmgr.pub.term_source = MyJpegTermSource;
  srcmgr.pub.next_input_byte = srcmgr.buffer;
  srcmgr.pub.bytes_in_buffer = 0;
  srcmgr.stream = &_stream;

  jpeg.read_header(&cinfo, true);
  jpeg.calc_output_dimensions(&cinfo);

  _size.w = cinfo.output_width;
  _size.h = cinfo.output_height;
  _planes = 1;
  _actualFrame = 0;
  _framesCount = 1;

  // Check whether the image size is valid.
  if (!checkImageSize())
  {
    err = ERR_IMAGE_INVALID_SIZE;
    goto _End;
  }

  jpeg.start_decompress(&cinfo);

  // Set 8 or 24-bit output.
  if (cinfo.out_color_space == JCS_GRAYSCALE)
  {
    if (cinfo.output_components != 1)
    {
      err = ERR_IMAGEIO_UNSUPPORTED_FORMAT;
      goto _End;
    }

    format = IMAGE_FORMAT_I8;
    bpp = 1;
  }
  else if (cinfo.out_color_space == JCS_RGB)
  {
    if (cinfo.output_components != 3)
    {
      err = ERR_IMAGEIO_UNSUPPORTED_FORMAT;
      goto _End;
    }
  }
  else
  {
    cinfo.out_color_space = JCS_RGB;
    cinfo.quantize_colors = false;
  }

  // Create the image.
  if (FOG_IS_ERROR(err = image.create(_size, format))) goto _End;

  if (format == IMAGE_FORMAT_I8)
  {
    image.setPalette(ImagePalette::fromGreyscale(256));

    uint8_t* pixels = image.getFirstX();
    ssize_t stride = image.getStride();

    while (cinfo.output_scanline < cinfo.output_height)
    {
      rowptr[0] = (JSAMPROW)(pixels + (ssize_t)cinfo.output_scanline * stride);
      jpeg.read_scanlines(&cinfo, rowptr, (JDIMENSION)1);

      if ((cinfo.output_scanline & 15) == 0)
        updateProgress(cinfo.output_scanline, cinfo.output_height);
    }
  }
  else
  {
    ImageConverter converter;
    err = converter.create(
      ImageFormatDescription::getByFormat(format),
      ImageFormatDescription::fromArgb(24, IMAGE_FD_NONE, 0,
        FOG_JPEG_RGB24_RMASK,
        FOG_JPEG_RGB24_GMASK,
        FOG_JPEG_RGB24_BMASK));
    if (FOG_IS_ERROR(err)) goto _End;

    ImageConverterClosure closure;
    converter.setupClosure(&closure, PointI(0, 0));

    uint8_t* pixels = image.getFirstX();
    ssize_t stride = image.getStride();

    while (cinfo.output_scanline < cinfo.output_height)
    {
      rowptr[0] = (JSAMPROW)(pixels + (ssize_t)cinfo.output_scanline * stride);
      jpeg.read_scanlines(&cinfo, rowptr, (JDIMENSION)1);

      if (!converter.isCopy())
        converter.getBlitFn()((uint8_t*)rowptr[0], (uint8_t*)rowptr[0], _size.w, &closure);

      if ((cinfo.output_scanline & 15) == 0)
        updateProgress(cinfo.output_scanline, cinfo.output_height);

      closure.ditherOrigin.y++;
    }
  }

  jpeg.finish_decompress(&cinfo);

_End:
  jpeg.destroy_decompress(&cinfo);
  image._modified();

  return err;
}