| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace { |
| |
| struct PlaneViewerZoomer final : public ImageUtils::ShortcutZoomer { |
| PlaneViewerZoomer(PlaneViewer *planeViewer) : ShortcutZoomer(planeViewer) {} |
| |
| private: |
| bool zoom(bool zoomin, bool resetZoom) override; |
| bool fit() override; |
| }; |
| |
| |
| |
| bool PlaneViewerZoomer::zoom(bool zoomin, bool resetZoom) { |
| PlaneViewer &planeViewer = static_cast<PlaneViewer &>(*getWidget()); |
| |
| resetZoom ? planeViewer.resetView() : zoomin ? planeViewer.zoomIn() |
| : planeViewer.zoomOut(); |
| |
| return true; |
| } |
| |
| bool PlaneViewerZoomer::fit() { |
| PlaneViewer &planeViewer = static_cast<PlaneViewer &>(*getWidget()); |
| |
| planeViewer.fitView(); |
| |
| return true; |
| } |
| } |
| |
| |
| |
| PlaneViewer::PlaneViewer(QWidget *parent) |
| : GLWidgetForHighDpi(parent) |
| , m_firstResize(true) |
| , m_xpos(0) |
| , m_ypos(0) |
| , m_aff() |
| , m_chessSize(40.0) |
| , m_firstDraw(true) |
| , m_dpiX(0.0) |
| , m_dpiY(0.0) { |
| m_zoomRange[0] = 1e-3, m_zoomRange[1] = 1024.0; |
| setBgColor(TPixel32(235, 235, 235), TPixel32(235, 235, 235)); |
| |
| setAttribute(Qt::WA_AcceptTouchEvents); |
| grabGesture(Qt::SwipeGesture); |
| grabGesture(Qt::PanGesture); |
| grabGesture(Qt::PinchGesture); |
| } |
| |
| |
| |
| void PlaneViewer::setZoomRange(double zoomMin, double zoomMax) { |
| m_zoomRange[0] = zoomMin, m_zoomRange[1] = zoomMax; |
| } |
| |
| |
| |
| void PlaneViewer::setBgColor(const TPixel32 &color1, const TPixel32 &color2) { |
| m_bgColorF[0] = color1.r / 255.0, m_bgColorF[1] = color1.g / 255.0, |
| m_bgColorF[2] = color1.b / 255.0; |
| m_bgColorF[3] = color2.r / 255.0, m_bgColorF[4] = color2.g / 255.0, |
| m_bgColorF[5] = color2.b / 255.0; |
| } |
| |
| |
| |
| void PlaneViewer::getBgColor(TPixel32 &color1, TPixel32 &color2) const { |
| color1.r = m_bgColorF[0] * 255.0, color1.g = m_bgColorF[1] * 255.0, |
| color1.b = m_bgColorF[2] * 255.0; |
| color2.r = m_bgColorF[3] * 255.0, color2.g = m_bgColorF[4] * 255.0, |
| color2.b = m_bgColorF[5] * 255.0; |
| } |
| |
| |
| |
| void PlaneViewer::drawBackground() { |
| glClearColor(m_bgColorF[0], m_bgColorF[1], m_bgColorF[2], 1.0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| if (m_bgColorF[0] != m_bgColorF[3] || m_bgColorF[1] != m_bgColorF[4] || |
| m_bgColorF[2] != m_bgColorF[5]) { |
| |
| TRectD rect(winToWorld(0, 0), winToWorld(width(), height())); |
| |
| |
| TRect chessRect(tfloor(rect.x0 / m_chessSize), |
| tfloor(rect.y0 / m_chessSize), tceil(rect.x1 / m_chessSize), |
| tceil(rect.y1 / m_chessSize)); |
| |
| |
| glColor3f(m_bgColorF[3], m_bgColorF[4], m_bgColorF[5]); |
| glBegin(GL_QUADS); |
| |
| int x, y; |
| TPointD pos; |
| double chessSize2 = 2.0 * m_chessSize; |
| |
| for (y = chessRect.y0; y < chessRect.y1; ++y) { |
| pos.y = y * m_chessSize; |
| for (x = chessRect.x0 + ((chessRect.x0 + y) % 2), pos.x = x * m_chessSize; |
| x < chessRect.x1; x += 2, pos.x += chessSize2) { |
| glVertex2d(pos.x, pos.y); |
| glVertex2d(pos.x + m_chessSize, pos.y); |
| glVertex2d(pos.x + m_chessSize, pos.y + m_chessSize); |
| glVertex2d(pos.x, pos.y + m_chessSize); |
| } |
| } |
| |
| glEnd(); |
| } |
| } |
| |
| |
| |
| void PlaneViewer::initializeGL() { initializeOpenGLFunctions(); } |
| |
| void PlaneViewer::resizeGL(int width, int height) { |
| width *= getDevPixRatio(); |
| height *= getDevPixRatio(); |
| glViewport(0, 0, width, height); |
| |
| glMatrixMode(GL_PROJECTION); |
| glLoadIdentity(); |
| gluOrtho2D(0, width, 0, height); |
| |
| glMatrixMode(GL_MODELVIEW); |
| glLoadIdentity(); |
| |
| if (m_firstResize) { |
| m_firstResize = false; |
| m_aff = TTranslation(0.5 * width, 0.5 * height); |
| m_width = width; |
| m_height = height; |
| } else { |
| TPointD oldCenter(m_width * 0.5, m_height * 0.5); |
| TPointD newCenter(width * 0.5, height * 0.5); |
| |
| m_aff = m_aff.place(m_aff.inv() * oldCenter, newCenter); |
| m_width = width; |
| m_height = height; |
| } |
| } |
| |
| |
| |
| void PlaneViewer::mouseMoveEvent(QMouseEvent *event) { |
| if (m_gestureActive && m_touchDevice == QTouchDevice::TouchScreen && |
| !m_stylusUsed) { |
| return; |
| } |
| |
| QPoint curPos = event->pos() * getDevPixRatio(); |
| if (event->buttons() & Qt::MidButton) |
| moveView(curPos.x() - m_xpos, height() - curPos.y() - m_ypos); |
| |
| m_xpos = curPos.x(), m_ypos = height() - curPos.y(); |
| } |
| |
| |
| |
| void PlaneViewer::mousePressEvent(QMouseEvent *event) { |
| |
| if (m_gestureActive && m_touchDevice == QTouchDevice::TouchScreen && |
| !m_stylusUsed) { |
| return; |
| } |
| |
| m_xpos = event->x() * getDevPixRatio(); |
| m_ypos = height() - event->y() * getDevPixRatio(); |
| } |
| |
| |
| |
| void PlaneViewer::mouseDoubleClickEvent(QMouseEvent *event) { |
| |
| if (m_gestureActive && !m_stylusUsed) { |
| m_gestureActive = false; |
| fitView(); |
| return; |
| } |
| } |
| |
| |
| |
| void PlaneViewer::mouseReleaseEvent(QMouseEvent *event) { |
| m_gestureActive = false; |
| m_zooming = false; |
| m_panning = false; |
| m_stylusUsed = false; |
| } |
| |
| |
| |
| void PlaneViewer::wheelEvent(QWheelEvent *event) { |
| int delta = 0; |
| switch (event->source()) { |
| case Qt::MouseEventNotSynthesized: { |
| if (event->modifiers() & Qt::AltModifier) |
| delta = event->angleDelta().x(); |
| else |
| delta = event->angleDelta().y(); |
| break; |
| } |
| |
| case Qt::MouseEventSynthesizedBySystem: { |
| QPoint numPixels = event->pixelDelta(); |
| QPoint numDegrees = event->angleDelta() / 8; |
| if (!numPixels.isNull()) { |
| delta = event->pixelDelta().y(); |
| } else if (!numDegrees.isNull()) { |
| QPoint numSteps = numDegrees / 15; |
| delta = numSteps.y(); |
| } |
| break; |
| } |
| |
| default: |
| |
| { |
| std::cout << "not supported event: Qt::MouseEventSynthesizedByQt, " |
| "Qt::MouseEventSynthesizedByApplication" |
| << std::endl; |
| break; |
| } |
| |
| } |
| |
| if (abs(delta) > 0) { |
| if ((m_gestureActive == true && |
| m_touchDevice == QTouchDevice::TouchScreen) || |
| m_gestureActive == false) { |
| TPointD pos(event->x() * getDevPixRatio(), |
| height() - event->y() * getDevPixRatio()); |
| double zoom_par = 1 + event->delta() * 0.001; |
| |
| zoomView(pos.x, pos.y, zoom_par); |
| } |
| } |
| event->accept(); |
| } |
| |
| |
| |
| void PlaneViewer::keyPressEvent(QKeyEvent *event) { |
| if (PlaneViewerZoomer(this).exec(event)) return; |
| |
| QOpenGLWidget::keyPressEvent(event); |
| } |
| |
| |
| |
| |
| void PlaneViewer::hideEvent(QHideEvent *event) { |
| m_rasterBuffer = TRaster32P(); |
| m_dpiX = 0.0; |
| m_dpiY = 0.0; |
| } |
| |
| |
| |
| void PlaneViewer::contextMenuEvent(QContextMenuEvent *event) { |
| QMenu *menu = new QMenu(this); |
| |
| QAction *reset = menu->addAction(tr("Reset View")); |
| reset->setShortcut( |
| QKeySequence(CommandManager::instance()->getKeyFromId(V_ZoomReset))); |
| connect(reset, SIGNAL(triggered()), SLOT(resetView())); |
| |
| QAction *fit = menu->addAction(tr("Fit To Window")); |
| fit->setShortcut( |
| QKeySequence(CommandManager::instance()->getKeyFromId(V_ZoomFit))); |
| connect(fit, SIGNAL(triggered()), SLOT(fitView())); |
| |
| menu->exec(event->globalPos()); |
| |
| delete menu; |
| update(); |
| } |
| |
| |
| |
| void PlaneViewer::tabletEvent(QTabletEvent *e) { |
| |
| if (e->type() == QTabletEvent::TabletPress) { |
| m_stylusUsed = e->pointerType() ? true : false; |
| } else if (e->type() == QTabletEvent::TabletRelease) { |
| m_stylusUsed = false; |
| } |
| |
| e->accept(); |
| } |
| |
| |
| |
| void PlaneViewer::gestureEvent(QGestureEvent *e) { |
| |
| m_gestureActive = false; |
| if (QGesture *swipe = e->gesture(Qt::SwipeGesture)) { |
| m_gestureActive = true; |
| } else if (QGesture *pan = e->gesture(Qt::PanGesture)) { |
| m_gestureActive = true; |
| } |
| if (QGesture *pinch = e->gesture(Qt::PinchGesture)) { |
| QPinchGesture *gesture = static_cast<QPinchGesture *>(pinch); |
| QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags(); |
| QPoint firstCenter = gesture->centerPoint().toPoint(); |
| if (m_touchDevice == QTouchDevice::TouchScreen) |
| firstCenter = mapFromGlobal(firstCenter); |
| |
| if (gesture->state() == Qt::GestureStarted) { |
| m_gestureActive = true; |
| } else if (gesture->state() == Qt::GestureFinished) { |
| m_gestureActive = false; |
| m_zooming = false; |
| m_scaleFactor = 0.0; |
| } else { |
| if (changeFlags & QPinchGesture::ScaleFactorChanged) { |
| double scaleFactor = gesture->scaleFactor(); |
| |
| |
| if (scaleFactor > 1) { |
| double decimalValue = scaleFactor - 1; |
| decimalValue /= 1.5; |
| scaleFactor = 1 + decimalValue; |
| } else if (scaleFactor < 1) { |
| double decimalValue = 1 - scaleFactor; |
| decimalValue /= 1.5; |
| scaleFactor = 1 - decimalValue; |
| } |
| if (!m_zooming) { |
| double delta = scaleFactor - 1; |
| m_scaleFactor += delta; |
| if (m_scaleFactor > .2 || m_scaleFactor < -.2) { |
| m_zooming = true; |
| } |
| } |
| if (m_zooming) { |
| zoomView(firstCenter.x() * getDevPixRatio(), |
| firstCenter.y() * getDevPixRatio(), scaleFactor); |
| m_panning = false; |
| } |
| m_gestureActive = true; |
| } |
| |
| if (changeFlags & QPinchGesture::CenterPointChanged) { |
| QPointF centerDelta = (gesture->centerPoint() * getDevPixRatio()) - |
| (gesture->lastCenterPoint() * getDevPixRatio()); |
| if (centerDelta.manhattanLength() > 1) { |
| |
| } |
| m_gestureActive = true; |
| } |
| } |
| } |
| e->accept(); |
| } |
| |
| void PlaneViewer::touchEvent(QTouchEvent *e, int type) { |
| |
| if (type == QEvent::TouchBegin) { |
| m_touchActive = true; |
| m_firstPanPoint = e->touchPoints().at(0).pos(); |
| |
| m_touchDevice = e->device()->type(); |
| } else if (m_touchActive) { |
| |
| |
| if ((e->touchPoints().count() == 2 && |
| m_touchDevice == QTouchDevice::TouchPad) || |
| (e->touchPoints().count() == 1 && |
| m_touchDevice == QTouchDevice::TouchScreen)) { |
| QTouchEvent::TouchPoint panPoint = e->touchPoints().at(0); |
| if (!m_panning) { |
| QPointF deltaPoint = panPoint.pos() - m_firstPanPoint; |
| |
| |
| if ((deltaPoint.manhattanLength() > 100) && !m_zooming) { |
| m_panning = true; |
| } |
| } |
| if (m_panning) { |
| QPoint curPos = panPoint.pos().toPoint() * getDevPixRatio(); |
| QPoint lastPos = panPoint.lastPos().toPoint() * getDevPixRatio(); |
| QPoint centerDelta = curPos - lastPos; |
| moveView(centerDelta.x(), -centerDelta.y()); |
| } |
| } |
| } |
| if (type == QEvent::TouchEnd || type == QEvent::TouchCancel) { |
| m_touchActive = false; |
| m_panning = false; |
| } |
| e->accept(); |
| } |
| |
| bool PlaneViewer::event(QEvent *e) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (e->type() == QEvent::Gesture && |
| CommandManager::instance() |
| ->getAction(MI_TouchGestureControl) |
| ->isChecked()) { |
| gestureEvent(static_cast<QGestureEvent *>(e)); |
| return true; |
| } |
| if ((e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchEnd || |
| e->type() == QEvent::TouchCancel || e->type() == QEvent::TouchUpdate) && |
| CommandManager::instance() |
| ->getAction(MI_TouchGestureControl) |
| ->isChecked()) { |
| touchEvent(static_cast<QTouchEvent *>(e), e->type()); |
| m_gestureActive = true; |
| return true; |
| } |
| return GLWidgetForHighDpi::event(e); |
| } |
| |
| |
| |
| void PlaneViewer::resetView() { |
| m_aff = TTranslation(0.5 * width(), 0.5 * height()); |
| update(); |
| } |
| |
| void PlaneViewer::fitView() { |
| if (m_imageBounds.isEmpty()) return; |
| m_aff = TTranslation(0.5 * width(), 0.5 * height()); |
| |
| double imageScale = std::min(width() / (double)m_imageBounds.getLx(), |
| height() / (double)m_imageBounds.getLy()); |
| |
| m_aff = TScale(imageScale, imageScale); |
| if (m_dpiX != 0.0 && m_dpiY != 0.0) |
| m_aff *= TScale(m_dpiX / Stage::inch, m_dpiY / Stage::inch); |
| m_aff.a13 = 0.5 * width(); |
| m_aff.a23 = 0.5 * height(); |
| |
| update(); |
| } |
| |
| |
| |
| void PlaneViewer::setViewPos(double x, double y) { |
| m_aff.a13 = x, m_aff.a23 = y; |
| update(); |
| |
| |
| qDebug("zoom = %.4f; pos = %.4f, %.4f", m_aff.a11, m_aff.a13, m_aff.a23); |
| |
| } |
| |
| |
| |
| void PlaneViewer::setViewZoom(double x, double y, double zoom) { |
| zoom = tcrop(zoom, m_zoomRange[0], m_zoomRange[1]); |
| double delta = zoom / m_aff.a11; |
| |
| m_aff.a13 = x + delta * (m_aff.a13 - x); |
| m_aff.a23 = y + delta * (m_aff.a23 - y); |
| m_aff.a11 = m_aff.a22 = zoom; |
| |
| update(); |
| |
| |
| qDebug("zoom = %.4f; pos = %.4f, %.4f", m_aff.a11, m_aff.a13, m_aff.a23); |
| |
| } |
| |
| |
| |
| void PlaneViewer::zoomIn() { |
| setViewZoom(ImageUtils::getQuantizedZoomFactor(m_aff.a11, true)); |
| } |
| |
| |
| |
| void PlaneViewer::zoomOut() { |
| setViewZoom(ImageUtils::getQuantizedZoomFactor(m_aff.a11, false)); |
| } |
| |
| |
| |
| void PlaneViewer::pushGLWorldCoordinates() { |
| m_matrix[0] = m_aff.a11; |
| m_matrix[4] = m_aff.a12; |
| m_matrix[12] = m_aff.a13; |
| m_matrix[1] = m_aff.a21; |
| m_matrix[5] = m_aff.a22; |
| m_matrix[13] = m_aff.a23; |
| |
| m_matrix[2] = m_matrix[3] = m_matrix[6] = m_matrix[7] = m_matrix[8] = |
| m_matrix[9] = m_matrix[10] = m_matrix[11] = m_matrix[14] = 0; |
| |
| m_matrix[15] = 1.0; |
| |
| glPushMatrix(); |
| glLoadMatrixd(m_matrix); |
| } |
| |
| |
| |
| void PlaneViewer::pushGLWinCoordinates() { |
| glPushMatrix(); |
| glLoadIdentity(); |
| } |
| |
| |
| |
| void PlaneViewer::popGLCoordinates() { glPopMatrix(); } |
| |
| |
| |
| TRaster32P PlaneViewer::rasterBuffer() { |
| if (!m_rasterBuffer || m_rasterBuffer->getLx() != width() || |
| m_rasterBuffer->getLy() != height()) |
| m_rasterBuffer = TRaster32P(width(), height()); |
| |
| return m_rasterBuffer; |
| } |
| |
| |
| |
| void PlaneViewer::flushRasterBuffer() { |
| assert(m_rasterBuffer); |
| |
| glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| glRasterPos2d(0, 0); |
| glDrawPixels(width(), height(), TGL_FMT, TGL_TYPE, |
| m_rasterBuffer->getRawData()); |
| } |
| |
| |
| |
| void PlaneViewer::draw(TRasterP ras, double dpiX, double dpiY, TPalette *pal) { |
| TPointD rasCenter(ras->getCenterD()); |
| |
| TRaster32P aux(rasterBuffer()); |
| |
| m_imageBounds = ras->getBounds(); |
| if (m_dpiX == 0.0 || m_dpiY == 0.0) { |
| m_dpiX = dpiX; |
| m_dpiY = dpiY; |
| } |
| if (m_firstDraw && !m_imageBounds.isEmpty()) { |
| m_firstDraw = false; |
| fitView(); |
| } |
| |
| aux->lock(); |
| ras->lock(); |
| |
| glGetDoublev(GL_MODELVIEW_MATRIX, m_matrix); |
| TAffine viewAff(m_matrix[0], m_matrix[4], m_matrix[12], m_matrix[1], |
| m_matrix[5], m_matrix[13]); |
| viewAff = viewAff * TScale(Stage::inch / dpiX, Stage::inch / dpiY) * |
| TTranslation(-rasCenter); |
| |
| pushGLWinCoordinates(); |
| |
| aux->clear(); |
| if (pal) |
| TRop::quickPut(aux, (TRasterCM32P)ras, pal, viewAff); |
| else |
| TRop::quickPut(aux, ras, viewAff); |
| |
| flushRasterBuffer(); |
| popGLCoordinates(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| void PlaneViewer::draw(TRasterImageP ri) { |
| double dpiX, dpiY; |
| ri->getDpi(dpiX, dpiY); |
| |
| if (dpiX == 0.0 || dpiY == 0.0) dpiX = dpiY = Stage::inch; |
| |
| draw(ri->getRaster(), dpiX, dpiY); |
| } |
| |
| |
| |
| void PlaneViewer::draw(TToonzImageP ti) { |
| double dpiX, dpiY; |
| ti->getDpi(dpiX, dpiY); |
| |
| if (dpiX == 0.0 || dpiY == 0.0) dpiX = dpiY = Stage::inch; |
| |
| draw(ti->getRaster(), dpiX, dpiY, ti->getPalette()); |
| } |
| |
| |
| |
| void PlaneViewer::draw(TVectorImageP vi) { |
| TRectD bbox(vi->getBBox()); |
| TRect bboxI(tfloor(bbox.x0), tfloor(bbox.y0), tceil(bbox.x1) - 1, |
| tceil(bbox.y1) - 1); |
| m_imageBounds = bboxI; |
| if (m_dpiX == 0.0 || m_dpiY == 0.0) { |
| m_dpiX = Stage::inch; |
| m_dpiY = Stage::inch; |
| } |
| if (m_firstDraw) { |
| m_firstDraw = false; |
| fitView(); |
| } |
| TVectorRenderData rd(TAffine(), bboxI, vi->getPalette(), 0, true, true); |
| tglDraw(rd, vi.getPointer()); |
| } |
| |
| |
| |
| void PlaneViewer::draw(TImageP img) { |
| { |
| TRasterImageP ri(img); |
| if (ri) { |
| draw(ri); |
| return; |
| } |
| } |
| { |
| TToonzImageP ti(img); |
| if (ti) { |
| draw(ti); |
| return; |
| } |
| } |
| { |
| TVectorImageP vi(img); |
| if (vi) { |
| draw(vi); |
| return; |
| } |
| } |
| } |
| |