diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/DocStyle.cpp b/DocStyle.cpp index 77b229b..4bc0b97 100644 --- a/DocStyle.cpp +++ b/DocStyle.cpp @@ -1,5 +1,6 @@ #include #include "DocStyle.h" +#include "Util.h" struct TextRoleProperties diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/DocStyle.cpp b/DocStyle.cpp index 77b229b..4bc0b97 100644 --- a/DocStyle.cpp +++ b/DocStyle.cpp @@ -1,5 +1,6 @@ #include #include "DocStyle.h" +#include "Util.h" struct TextRoleProperties diff --git a/DocTemplate.cpp b/DocTemplate.cpp index ce9fea2..37787cb 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include #include "DocTemplate.h" @@ -10,12 +10,17 @@ unsigned int DocTemplate::m_currentLine = 0; -#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, __VA_ARGS__) -#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, __VA_ARGS__) -#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, __VA_ARGS__) +#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, ##__VA_ARGS__) +#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, ##__VA_ARGS__) +#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, ##__VA_ARGS__) -//#define HAVE_CONSTEXPR +#ifndef _WIN32 +# define HAVE_CONSTEXPR +#else +# include +#endif + #ifdef HAVE_CONSTEXPR // TODO: XXX ### This side of HAVE_CONSTEXPR has not been tested @@ -23,6 +28,10 @@ { return !str[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str[h]; } +unsigned int hashString(const std::string& str, int h = 0) +{ + return !str.c_str()[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str.c_str()[h]; +} // Assumes that for lower case comparing, the values passed in are already lower case #define Choose(x) do { std::string tttmp = x; switch (hashString(x)) { @@ -112,6 +121,7 @@ { va_list args; va_start(args, a_msg); +#ifdef _WIN32 auto sizeForFileLineAndNull = m_currentFile.size() + 64 + 1; auto sizeRequired = _vscprintf(a_msg, args) + sizeForFileLineAndNull; auto a = (char*)_alloca(sizeRequired); @@ -119,6 +129,9 @@ vsnprintf_s(a + offset, sizeRequired - offset, INT_MAX - 1, a_msg, args); OutputDebugStringA(a); _freea(a); +#else + vfprintf(stderr, a_msg, args); +#endif va_end(args); /* if (a_level == LL_Warning) @@ -141,7 +154,7 @@ } -std::string trim(std::string& str) +std::string trim(std::string str) { str.erase(0, str.find_first_not_of(" \t\r\n")); str.erase(str.find_last_not_of(" \t\r\n") + 1); @@ -149,7 +162,7 @@ } -std::string str2lower(std::string& str) +std::string str2lower(const std::string& str) { std::string ret = str; std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); @@ -157,7 +170,7 @@ } -unsigned int str2col(std::string& str) +unsigned int str2col(std::string str) { if (str[0] == '#') str = &str[1]; @@ -165,7 +178,7 @@ } -std::vector str2pointList(std::string& str) +std::vector str2pointList(const std::string& str) { std::vector ret; std::vector strs = split(str, ','); @@ -183,7 +196,7 @@ } -Point str2pos(std::string& str) +Point str2pos(const std::string& str) { Point ret = { 0, 0 }; std::vector strs = split(str, ','); @@ -193,13 +206,13 @@ } -float str2float(std::string& str) +float str2float(const std::string& str) { return (float)atof(str.c_str()); } -PenStyle str2style(std::string& str) +PenStyle str2style(const std::string& str) { Choose(str) { @@ -216,7 +229,7 @@ } -Align str2align(std::string& str) +Align str2align(const std::string& str) { unsigned int alignment = 0; std::vector strs = split(str, ','); @@ -269,6 +282,15 @@ } +#ifndef _WIN32 +int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} +#endif + + void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/DocStyle.cpp b/DocStyle.cpp index 77b229b..4bc0b97 100644 --- a/DocStyle.cpp +++ b/DocStyle.cpp @@ -1,5 +1,6 @@ #include #include "DocStyle.h" +#include "Util.h" struct TextRoleProperties diff --git a/DocTemplate.cpp b/DocTemplate.cpp index ce9fea2..37787cb 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include #include "DocTemplate.h" @@ -10,12 +10,17 @@ unsigned int DocTemplate::m_currentLine = 0; -#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, __VA_ARGS__) -#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, __VA_ARGS__) -#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, __VA_ARGS__) +#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, ##__VA_ARGS__) +#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, ##__VA_ARGS__) +#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, ##__VA_ARGS__) -//#define HAVE_CONSTEXPR +#ifndef _WIN32 +# define HAVE_CONSTEXPR +#else +# include +#endif + #ifdef HAVE_CONSTEXPR // TODO: XXX ### This side of HAVE_CONSTEXPR has not been tested @@ -23,6 +28,10 @@ { return !str[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str[h]; } +unsigned int hashString(const std::string& str, int h = 0) +{ + return !str.c_str()[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str.c_str()[h]; +} // Assumes that for lower case comparing, the values passed in are already lower case #define Choose(x) do { std::string tttmp = x; switch (hashString(x)) { @@ -112,6 +121,7 @@ { va_list args; va_start(args, a_msg); +#ifdef _WIN32 auto sizeForFileLineAndNull = m_currentFile.size() + 64 + 1; auto sizeRequired = _vscprintf(a_msg, args) + sizeForFileLineAndNull; auto a = (char*)_alloca(sizeRequired); @@ -119,6 +129,9 @@ vsnprintf_s(a + offset, sizeRequired - offset, INT_MAX - 1, a_msg, args); OutputDebugStringA(a); _freea(a); +#else + vfprintf(stderr, a_msg, args); +#endif va_end(args); /* if (a_level == LL_Warning) @@ -141,7 +154,7 @@ } -std::string trim(std::string& str) +std::string trim(std::string str) { str.erase(0, str.find_first_not_of(" \t\r\n")); str.erase(str.find_last_not_of(" \t\r\n") + 1); @@ -149,7 +162,7 @@ } -std::string str2lower(std::string& str) +std::string str2lower(const std::string& str) { std::string ret = str; std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); @@ -157,7 +170,7 @@ } -unsigned int str2col(std::string& str) +unsigned int str2col(std::string str) { if (str[0] == '#') str = &str[1]; @@ -165,7 +178,7 @@ } -std::vector str2pointList(std::string& str) +std::vector str2pointList(const std::string& str) { std::vector ret; std::vector strs = split(str, ','); @@ -183,7 +196,7 @@ } -Point str2pos(std::string& str) +Point str2pos(const std::string& str) { Point ret = { 0, 0 }; std::vector strs = split(str, ','); @@ -193,13 +206,13 @@ } -float str2float(std::string& str) +float str2float(const std::string& str) { return (float)atof(str.c_str()); } -PenStyle str2style(std::string& str) +PenStyle str2style(const std::string& str) { Choose(str) { @@ -216,7 +229,7 @@ } -Align str2align(std::string& str) +Align str2align(const std::string& str) { unsigned int alignment = 0; std::vector strs = split(str, ','); @@ -269,6 +282,15 @@ } +#ifndef _WIN32 +int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} +#endif + + void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index cba2de9..d7ca6d2 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -15,12 +15,17 @@ class GenericTableView : public QTableView { public: - GenericTableView(GenericTableBase* a_table, QWidget* a_parent) : QTableView(a_parent), m_genericTable(a_table) - { - setAcceptDrops(m_genericTable->m_readOnly ? false : true); + GenericTableView() : QTableView(nullptr), m_genericTable(nullptr) { setDragDropMode(QAbstractItemView::InternalMove); } + void init(GenericTableBase* a_table, QWidget* a_parent) + { + setParent(a_parent); + m_genericTable = a_table; + setAcceptDrops(m_genericTable->m_readOnly ? false : true); + } + void dragEnterEvent(QDragEnterEvent *event) { if (!m_genericTable->m_readOnly) { @@ -55,8 +60,14 @@ class GenericTableUI : public QAbstractTableModel { public: - GenericTableUI(GenericTableBase* a_table, QTableView* a_view) : QAbstractTableModel(a_view), m_genericTable(a_table) + GenericTableUI() : QAbstractTableModel(nullptr), m_genericTable(nullptr), m_view(nullptr) { + } + + void init(GenericTableBase* a_table, QTableView* a_view) + { + setParent(a_view); + m_genericTable = a_table; m_view = a_view; m_view->setModel(this); m_genericTable->RegisterCallbackOnChange(updateCallback, this); diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/DocStyle.cpp b/DocStyle.cpp index 77b229b..4bc0b97 100644 --- a/DocStyle.cpp +++ b/DocStyle.cpp @@ -1,5 +1,6 @@ #include #include "DocStyle.h" +#include "Util.h" struct TextRoleProperties diff --git a/DocTemplate.cpp b/DocTemplate.cpp index ce9fea2..37787cb 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include #include "DocTemplate.h" @@ -10,12 +10,17 @@ unsigned int DocTemplate::m_currentLine = 0; -#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, __VA_ARGS__) -#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, __VA_ARGS__) -#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, __VA_ARGS__) +#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, ##__VA_ARGS__) +#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, ##__VA_ARGS__) +#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, ##__VA_ARGS__) -//#define HAVE_CONSTEXPR +#ifndef _WIN32 +# define HAVE_CONSTEXPR +#else +# include +#endif + #ifdef HAVE_CONSTEXPR // TODO: XXX ### This side of HAVE_CONSTEXPR has not been tested @@ -23,6 +28,10 @@ { return !str[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str[h]; } +unsigned int hashString(const std::string& str, int h = 0) +{ + return !str.c_str()[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str.c_str()[h]; +} // Assumes that for lower case comparing, the values passed in are already lower case #define Choose(x) do { std::string tttmp = x; switch (hashString(x)) { @@ -112,6 +121,7 @@ { va_list args; va_start(args, a_msg); +#ifdef _WIN32 auto sizeForFileLineAndNull = m_currentFile.size() + 64 + 1; auto sizeRequired = _vscprintf(a_msg, args) + sizeForFileLineAndNull; auto a = (char*)_alloca(sizeRequired); @@ -119,6 +129,9 @@ vsnprintf_s(a + offset, sizeRequired - offset, INT_MAX - 1, a_msg, args); OutputDebugStringA(a); _freea(a); +#else + vfprintf(stderr, a_msg, args); +#endif va_end(args); /* if (a_level == LL_Warning) @@ -141,7 +154,7 @@ } -std::string trim(std::string& str) +std::string trim(std::string str) { str.erase(0, str.find_first_not_of(" \t\r\n")); str.erase(str.find_last_not_of(" \t\r\n") + 1); @@ -149,7 +162,7 @@ } -std::string str2lower(std::string& str) +std::string str2lower(const std::string& str) { std::string ret = str; std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); @@ -157,7 +170,7 @@ } -unsigned int str2col(std::string& str) +unsigned int str2col(std::string str) { if (str[0] == '#') str = &str[1]; @@ -165,7 +178,7 @@ } -std::vector str2pointList(std::string& str) +std::vector str2pointList(const std::string& str) { std::vector ret; std::vector strs = split(str, ','); @@ -183,7 +196,7 @@ } -Point str2pos(std::string& str) +Point str2pos(const std::string& str) { Point ret = { 0, 0 }; std::vector strs = split(str, ','); @@ -193,13 +206,13 @@ } -float str2float(std::string& str) +float str2float(const std::string& str) { return (float)atof(str.c_str()); } -PenStyle str2style(std::string& str) +PenStyle str2style(const std::string& str) { Choose(str) { @@ -216,7 +229,7 @@ } -Align str2align(std::string& str) +Align str2align(const std::string& str) { unsigned int alignment = 0; std::vector strs = split(str, ','); @@ -269,6 +282,15 @@ } +#ifndef _WIN32 +int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} +#endif + + void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index cba2de9..d7ca6d2 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -15,12 +15,17 @@ class GenericTableView : public QTableView { public: - GenericTableView(GenericTableBase* a_table, QWidget* a_parent) : QTableView(a_parent), m_genericTable(a_table) - { - setAcceptDrops(m_genericTable->m_readOnly ? false : true); + GenericTableView() : QTableView(nullptr), m_genericTable(nullptr) { setDragDropMode(QAbstractItemView::InternalMove); } + void init(GenericTableBase* a_table, QWidget* a_parent) + { + setParent(a_parent); + m_genericTable = a_table; + setAcceptDrops(m_genericTable->m_readOnly ? false : true); + } + void dragEnterEvent(QDragEnterEvent *event) { if (!m_genericTable->m_readOnly) { @@ -55,8 +60,14 @@ class GenericTableUI : public QAbstractTableModel { public: - GenericTableUI(GenericTableBase* a_table, QTableView* a_view) : QAbstractTableModel(a_view), m_genericTable(a_table) + GenericTableUI() : QAbstractTableModel(nullptr), m_genericTable(nullptr), m_view(nullptr) { + } + + void init(GenericTableBase* a_table, QTableView* a_view) + { + setParent(a_view); + m_genericTable = a_table; m_view = a_view; m_view->setModel(this); m_genericTable->RegisterCallbackOnChange(updateCallback, this); diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index bdf2b44..7680de9 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -38,7 +38,8 @@ ../DocProject.h \ ../DocStyle.h \ ../DocTemplate.h \ - ../DocVisitor.h + ../DocVisitor.h \ + ../DocSVG.h SOURCES += \ ../Framework/Tests.cpp \ @@ -51,6 +52,7 @@ ../DocStyle.cpp \ ../DocTemplate.cpp \ ../DocVisitor.cpp \ + ../DocSVG.cpp \ ../main.cpp SOURCES += \ diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/DocStyle.cpp b/DocStyle.cpp index 77b229b..4bc0b97 100644 --- a/DocStyle.cpp +++ b/DocStyle.cpp @@ -1,5 +1,6 @@ #include #include "DocStyle.h" +#include "Util.h" struct TextRoleProperties diff --git a/DocTemplate.cpp b/DocTemplate.cpp index ce9fea2..37787cb 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include #include "DocTemplate.h" @@ -10,12 +10,17 @@ unsigned int DocTemplate::m_currentLine = 0; -#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, __VA_ARGS__) -#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, __VA_ARGS__) -#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, __VA_ARGS__) +#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, ##__VA_ARGS__) +#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, ##__VA_ARGS__) +#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, ##__VA_ARGS__) -//#define HAVE_CONSTEXPR +#ifndef _WIN32 +# define HAVE_CONSTEXPR +#else +# include +#endif + #ifdef HAVE_CONSTEXPR // TODO: XXX ### This side of HAVE_CONSTEXPR has not been tested @@ -23,6 +28,10 @@ { return !str[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str[h]; } +unsigned int hashString(const std::string& str, int h = 0) +{ + return !str.c_str()[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str.c_str()[h]; +} // Assumes that for lower case comparing, the values passed in are already lower case #define Choose(x) do { std::string tttmp = x; switch (hashString(x)) { @@ -112,6 +121,7 @@ { va_list args; va_start(args, a_msg); +#ifdef _WIN32 auto sizeForFileLineAndNull = m_currentFile.size() + 64 + 1; auto sizeRequired = _vscprintf(a_msg, args) + sizeForFileLineAndNull; auto a = (char*)_alloca(sizeRequired); @@ -119,6 +129,9 @@ vsnprintf_s(a + offset, sizeRequired - offset, INT_MAX - 1, a_msg, args); OutputDebugStringA(a); _freea(a); +#else + vfprintf(stderr, a_msg, args); +#endif va_end(args); /* if (a_level == LL_Warning) @@ -141,7 +154,7 @@ } -std::string trim(std::string& str) +std::string trim(std::string str) { str.erase(0, str.find_first_not_of(" \t\r\n")); str.erase(str.find_last_not_of(" \t\r\n") + 1); @@ -149,7 +162,7 @@ } -std::string str2lower(std::string& str) +std::string str2lower(const std::string& str) { std::string ret = str; std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); @@ -157,7 +170,7 @@ } -unsigned int str2col(std::string& str) +unsigned int str2col(std::string str) { if (str[0] == '#') str = &str[1]; @@ -165,7 +178,7 @@ } -std::vector str2pointList(std::string& str) +std::vector str2pointList(const std::string& str) { std::vector ret; std::vector strs = split(str, ','); @@ -183,7 +196,7 @@ } -Point str2pos(std::string& str) +Point str2pos(const std::string& str) { Point ret = { 0, 0 }; std::vector strs = split(str, ','); @@ -193,13 +206,13 @@ } -float str2float(std::string& str) +float str2float(const std::string& str) { return (float)atof(str.c_str()); } -PenStyle str2style(std::string& str) +PenStyle str2style(const std::string& str) { Choose(str) { @@ -216,7 +229,7 @@ } -Align str2align(std::string& str) +Align str2align(const std::string& str) { unsigned int alignment = 0; std::vector strs = split(str, ','); @@ -269,6 +282,15 @@ } +#ifndef _WIN32 +int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} +#endif + + void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index cba2de9..d7ca6d2 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -15,12 +15,17 @@ class GenericTableView : public QTableView { public: - GenericTableView(GenericTableBase* a_table, QWidget* a_parent) : QTableView(a_parent), m_genericTable(a_table) - { - setAcceptDrops(m_genericTable->m_readOnly ? false : true); + GenericTableView() : QTableView(nullptr), m_genericTable(nullptr) { setDragDropMode(QAbstractItemView::InternalMove); } + void init(GenericTableBase* a_table, QWidget* a_parent) + { + setParent(a_parent); + m_genericTable = a_table; + setAcceptDrops(m_genericTable->m_readOnly ? false : true); + } + void dragEnterEvent(QDragEnterEvent *event) { if (!m_genericTable->m_readOnly) { @@ -55,8 +60,14 @@ class GenericTableUI : public QAbstractTableModel { public: - GenericTableUI(GenericTableBase* a_table, QTableView* a_view) : QAbstractTableModel(a_view), m_genericTable(a_table) + GenericTableUI() : QAbstractTableModel(nullptr), m_genericTable(nullptr), m_view(nullptr) { + } + + void init(GenericTableBase* a_table, QTableView* a_view) + { + setParent(a_view); + m_genericTable = a_table; m_view = a_view; m_view->setModel(this); m_genericTable->RegisterCallbackOnChange(updateCallback, this); diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index bdf2b44..7680de9 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -38,7 +38,8 @@ ../DocProject.h \ ../DocStyle.h \ ../DocTemplate.h \ - ../DocVisitor.h + ../DocVisitor.h \ + ../DocSVG.h SOURCES += \ ../Framework/Tests.cpp \ @@ -51,6 +52,7 @@ ../DocStyle.cpp \ ../DocTemplate.cpp \ ../DocVisitor.cpp \ + ../DocSVG.cpp \ ../main.cpp SOURCES += \ diff --git a/main.cpp b/main.cpp index 6ff651f..be7a260 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ #include "DocVisitor.h" #include "DocTemplate.h" #include "DocOutput.h" +#include "DocSVG.h" //#include "document.h" #include "html.h" @@ -25,6 +26,11 @@ #endif +#ifdef GUI_APP +# include "ui.h" +#endif + + #define DEF_IUNIT 1024 #define DEF_OUNIT 64 #define DEF_MAX_NESTING 16 @@ -108,6 +114,22 @@ } +void SVGTest(const char* a_fileName, DocOutputDevice* outputDoc) +{ + hoedown_buffer* inputBuffer = ReadInWholeFile(a_fileName); + if (!inputBuffer) + return; + // SVG xml parse + TiXmlDocument parser; + parser.Parse((char*)inputBuffer->data); + DocSVG visitor; + parser.Accept(&visitor); + visitor.DumpOperations(); + visitor.WriteTo(outputDoc); + hoedown_buffer_free(inputBuffer); +} + + void ConvertHTMLToPDF(uint8_t* inputBuffer, DocOutputDevice* outputDoc) { // xml parse @@ -119,6 +141,8 @@ parser.Parse((char*)inputBuffer); DocVisitor visitor(outputDoc, &style, &templ); parser.Accept(&visitor); + + SVGTest("test/test.svg", outputDoc); } @@ -153,480 +177,8 @@ } -#ifdef GUI_APP - -#include "ui_About.h" -#include "ui_ExtensibleObjectModelUI.h" -#include "EventLog.h" -#include "GenericTableUI.h" - - -class UiLogger : public Logger -{ -public: - EventLog* m_log; - void logMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_message) - { - m_log->LogMessage(a_location, a_message); - } -}; - - -struct UiContext -{ - Ui_MainWindow appWindow; - QMainWindow mainWindow; - - Ui_About about; - QDialog aboutDialog; - - EventLog log; - UiLogger logger; - - void setupUi() - { - logger.m_log = &log; - g_currentModule.m_messageLogger = &logger; - - appWindow.setupUi(&mainWindow); - mainWindow.menuBar()->setNativeMenuBar(false); - - about.setupUi(&aboutDialog); - aboutDialog.setFixedSize(430, 300); - - QObject::connect(appWindow.actionAbout, SIGNAL(triggered(bool)), &aboutDialog, SLOT(exec())); - - mainWindow.show(); - } -}; - - -#include - - -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; - - -struct ColorScheme -{ - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; - - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } -}; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); - - -class SchemeSelection : public QWidget -{ -public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } -}; - - - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget -{ -public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } -}; - - -#endif - - int main(int argc, char* argv[]) { -#ifdef GUI_APP - QApplication app(argc, argv); - UiContext ui; - ui.setupUi(); - - ColorPicker picker; - picker.show(); - - - runTests(); - - YQ_LOG_DEBUG("This is a test message"); - ui.log.LogMessage("blah", "hooking up ui"); - - GenericTableUI table(&ui.log, ui.appWindow.eventLog); - - ui.log.LogMessage("blah", "starting"); - ui.log.LogMessage("blah", "starting1"); - ui.log.LogMessage("blah", "starting2"); - ui.log.LogMessage("blah", "starting3"); - ui.log.LogMessage("blah", "starting4"); - ui.log.LogMessage("blah", "starting"); - ui.log.LogMessage("blah", "true"); - ui.log.LogMessage("false", "starting"); - - return app.exec(); -#else DocOutputDevice outputDoc; Context context = { "Test Markdown to PDF", @@ -636,7 +188,16 @@ &outputDoc }; ProcessConversionContext(&context); - return 0; + +#ifdef GUI_APP + QApplication app(argc, argv); + UiContext ui; + ui.setupUi(); + YQ_LOG_DEBUG("This is a test message"); + runTests(); + app.exec(); #endif + + return 0; } diff --git a/ColorPicker.h b/ColorPicker.h new file mode 100644 index 0000000..c45c75a --- /dev/null +++ b/ColorPicker.h @@ -0,0 +1,393 @@ +#include + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + +bool m_wheelCached = false; +QImage colorWheelCache; + +void makeColorWheel(int siz) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; +const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +struct ColorScheme +{ + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName() + { + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; + } + int hueCount() + { + return modeHands[m_mode]; + } + void setHue(int a_index, float a_val) + { + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; + } + float denormalizedHue(int a_index) + { + if (a_index == 0) + return float(m_primaryHue) / 255.0; + if (a_index == 1 && m_mode != 2) + return 0.5 + float(m_primaryHue) / 255.0; + if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; + if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; + } + float hue(int a_index) + { + float h = denormalizedHue(a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; + } +}; + + +static_assert(sizeof(ColorScheme) == 4, "bad size"); + + +class SchemeSelection : public QWidget +{ +public: + const int titleHeight = 20; + const int modes = 5; + + ColorScheme* scheme; + SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} + + void mousePressEvent(QMouseEvent* me) + { + for (int i = 0; i < modes; i++) + { + QRect r(16, titleHeight + 10 + i * 55, 50, 50); + if (r.contains(me->pos())) + { + scheme->m_mode = i; + parentWidget()->update(); + return; + } + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + p.drawText(3 + 0, 15, "Scheme"); + + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); + for (int i = 0; i < modes; i++) + { + p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); + QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; + + for (int j = 0; j < modeHands[i]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[i][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); + //pix.transformed(m, Qt::SmoothTransformation); + } + } + } +}; + + + +//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + +class ColorPicker : public QWidget +{ +public: + const float twoPi = 2 * acos(-1); + const int siz = 250; + const int cen = (siz / 2); + const int rad = (siz / 2) - 20; + const int iter = siz * 10; + const int dotSiz = 18;// 16; + const int dotAlpha = 96;// 64; + + const int titleHeight = 20; + + const int schemeWidth = 80; + const int wheelWidth = siz * 2;// 600; + + const int paletteWidth = 150; + const int paletteHeight = 350; + + const int xOff = schemeWidth; + const int yOff = titleHeight; + //const int modes = 6; + + int m_hueMovingIdx; + + + ColorScheme palette; + SchemeSelection scheme; + + + // Properties of a scheme: + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + + ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) + { + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); + m_hueMovingIdx = -1; + } + ~ColorPicker() + { + } + + void mousePressEvent(QMouseEvent* me) + { + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } + } + + void mouseReleaseEvent(QMouseEvent*) + { + m_hueMovingIdx = -1; + } + + void mouseMoveEvent(QMouseEvent* me) + { + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); +update(); + } + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + + p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); + p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz); + p.drawImage(xOff, yOff, colorWheelCache); + //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); + p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + + //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF444444); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); + //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } + + } +}; + diff --git a/DocSVG.cpp b/DocSVG.cpp new file mode 100644 index 0000000..76c80cd --- /dev/null +++ b/DocSVG.cpp @@ -0,0 +1,101 @@ +#include "DocSVG.h" + + +const char SVGOperation::PathOperationChar[] = +{ + 'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z', + 'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' ' +}; + + +const int SVGOperation::PathOperationArgs[] = +{ + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, + 2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0 +}; + + +DocSVG::DocSVG() +{ +} + + +DocSVG::~DocSVG() +{ +} + + +/// Visit a document. +bool DocSVG::VisitEnter( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a document. +bool DocSVG::VisitExit( const TiXmlDocument& doc ) +{ + return true; +} + + +/// Visit a declaration +bool DocSVG::Visit( const TiXmlDeclaration& declaration ) +{ + return true; +} + + +/// Visit a stylesheet reference +bool DocSVG::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return true; +} + + +/// Visit a comment node +bool DocSVG::Visit( const TiXmlComment& comment ) +{ + return true; +} + + +/// Visit an unknow node +bool DocSVG::Visit( const TiXmlUnknown& unknown ) +{ + return true; +} + + +/// Visit an element. +bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { + const TiXmlAttribute* attrib = firstAttribute; + while ( attrib ) { + if (strcmp(attrib->Name(), "d") == 0) { + ParsePath(attrib->Value()); + break; + } else { + attrib = attrib->Next(); + } + } + } + + return true; +} + + +/// Visit an element. +bool DocSVG::VisitExit( const TiXmlElement& element ) +{ + return true; +} + + +/// Visit a text node +bool DocSVG::Visit( const TiXmlText& text ) +{ + return true; +} + diff --git a/DocSVG.h b/DocSVG.h new file mode 100644 index 0000000..08494bf --- /dev/null +++ b/DocSVG.h @@ -0,0 +1,301 @@ +#ifndef DOC_SVG_H +#define DOC_SVG_H + + +#include +//#include +#include "tinyxml.h" +#include "DocStyle.h" +#include "DocTemplate.h" +#include "DocOutput.h" +#include "hpdf.h" + + +class SVGOperation +{ +public: + enum PathOperation { + MoveToAbs, + LineToAbs, + HorizontalLineToAbs, + VerticalLineToAbs, + CurveToAbs, + SmoothCurveToAbs, + QuadraticBezierCurveToAbs, + SmoothQuadraticBezierCurveToAbs, + EllipticalArcToAbs, + ClosePath, + + MoveToRel, + LineToRel, + HorizontalLineToRel, + VerticalLineToRel, + CurveToRel, + SmoothCurveToRel, + QuadraticBezierCurveToRel, + SmoothQuadraticBezierCurveToRel, + EllipticalArcToRel, + AltClosePath, + + BadOperation, + + BeginPathOperation, + EndPathOperation + }; + + static const char PathOperationChar[]; + static const int PathOperationArgs[]; + + static PathOperation getOperationForChar(char a_ch) { + for (int i = 0; i < BadOperation; i++) + if (PathOperationChar[i] == a_ch) + return (PathOperation)i; + return BadOperation; + } + + int getArgCount() { + return PathOperationArgs[m_type]; + } + + void dump() { + fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]); + for (int i = 0; i < PathOperationArgs[m_type]; i++) + fprintf(stderr, " -%f- ", m_values[i]); + fprintf(stderr, "\n"); + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + float pageHeight = HPDF_Page_GetHeight(page); + float x = m_values[0]; + float y = m_values[1]; + switch (m_type) { + case MoveToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]); + break; + case LineToAbs: + curPos[0] = x; curPos[1] = y; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]); + break; + case HorizontalLineToAbs: + curPos[0] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToAbs: + curPos[1] = x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToAbs: + HPDF_Page_CurveTo(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3], + m_values[4], pageHeight - m_values[5]); + curPos[0] = m_values[4]; + curPos[1] = m_values[5]; + break; + case SmoothCurveToAbs: + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case QuadraticBezierCurveToAbs: + //HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3); + //HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3); + // HPDF_Page_CurveTo3 // ?? + HPDF_Page_CurveTo2(page, + m_values[0], pageHeight - m_values[1], + m_values[2], pageHeight - m_values[3]); + curPos[0] = m_values[2]; + curPos[1] = m_values[3]; + break; + case SmoothQuadraticBezierCurveToAbs: + // No idea, only has 2 args, I guess an x and y, but how does this make a curve? + break; + case EllipticalArcToAbs: + // SVG has 7 args, PDF has 5! + //HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y, + // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); + break; + case MoveToRel: + curPos[0] += x; curPos[1] += y; + HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); + break; + case LineToRel: + if (prevOp == BeginPathOperation) { + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case HorizontalLineToRel: + curPos[0] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case VerticalLineToRel: + curPos[1] += x; + HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); + break; + case CurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3], + curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]); + curPos[0] += m_values[4]; + curPos[1] += m_values[5]; + break; + case SmoothCurveToRel: + // TODO: check if these are cumulatively relative, or all rel to current pos + HPDF_Page_CurveTo2(page, + curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1], + curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]); + curPos[0] += m_values[2]; + curPos[1] += m_values[3]; + break; + case QuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case SmoothQuadraticBezierCurveToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case EllipticalArcToRel: + fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__); + break; + case ClosePath: + case AltClosePath: + default: + // TODO: actually this means to draw a line back to the first point in the path + /* + if (fill) + HPDF_Page_FillStroke(page); + else + HPDF_Page_Stroke(page); + */ + break; + } + } + + PathOperation m_type; + double m_values[7]; +}; + + +class DocSVG : public TiXmlVisitor +{ +public: + DocSVG(); + virtual ~DocSVG(); + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ); + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ); + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ); + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ); + /// Visit a text node + virtual bool Visit( const TiXmlText& text ); + + void DumpOperations() + { + for (int i = 0; i < operations.size(); i++) + operations[i].dump(); + } + + void WriteTo(DocOutputDevice* outputDoc) + { + DocOutputPage* outputPage = outputDoc->newPage(); + HPDF_Page page = outputPage->page(); + float curPos[2] = { 10.0, 10.0 }; + + + //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); + + HPDF_Page_EndText(page); + + HPDF_Page_SetLineWidth(page, 2.0); + HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API + //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); +/* + HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); + HPDF_Page_Circle(page, x, pageHeight - y, radius); +*/ + + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + operations[i].output(page, curPos, false, prevOp); + prevOp = operations[i].m_type; + } + + HPDF_Page_Stroke(page); // Need to close + HPDF_Page_BeginText(page); + + delete outputPage; + } + +private: + void ParsePath(const char* a_pathData) + { + SVGOperation currentOp; + currentOp.m_type = SVGOperation::BeginPathOperation; + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::MoveToAbs; + int val = 0; + int i = 0; + const char* pathPtr = a_pathData; + while (*pathPtr) { + char* end = 0; + double v = strtod(pathPtr, &end); + if ( end != pathPtr ) { + currentOp.m_values[val] = v; + val++; + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + val = 0; + } + pathPtr = end; + } else { + if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation) + { + if (val != 0) { + fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr); + break; + } + if (currentOp.getArgCount() == 0) // eg: ClosePath + operations.push_back(currentOp); + currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr); + } + pathPtr++; + } + } + if (val == currentOp.getArgCount()) { + operations.push_back(currentOp); + } else if (val) { + fprintf(stderr, "Broken SVG path data, expecting more numbers\n"); + } + currentOp.m_type = SVGOperation::EndPathOperation; + operations.push_back(currentOp); + } + + std::vector operations; +}; + + +#endif // DOC_SVG_H + + diff --git a/DocStyle.cpp b/DocStyle.cpp index 77b229b..4bc0b97 100644 --- a/DocStyle.cpp +++ b/DocStyle.cpp @@ -1,5 +1,6 @@ #include #include "DocStyle.h" +#include "Util.h" struct TextRoleProperties diff --git a/DocTemplate.cpp b/DocTemplate.cpp index ce9fea2..37787cb 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include #include "DocTemplate.h" @@ -10,12 +10,17 @@ unsigned int DocTemplate::m_currentLine = 0; -#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, __VA_ARGS__) -#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, __VA_ARGS__) -#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, __VA_ARGS__) +#define DebugMsg(fmt, ...) DocTemplate::LogMessage(LL_Debug, fmt, ##__VA_ARGS__) +#define WarningMsg(fmt, ...) DocTemplate::LogMessage(LL_Warning, fmt, ##__VA_ARGS__) +#define ErrorMsg(fmt, ...) DocTemplate::LogMessage(LL_Error, fmt, ##__VA_ARGS__) -//#define HAVE_CONSTEXPR +#ifndef _WIN32 +# define HAVE_CONSTEXPR +#else +# include +#endif + #ifdef HAVE_CONSTEXPR // TODO: XXX ### This side of HAVE_CONSTEXPR has not been tested @@ -23,6 +28,10 @@ { return !str[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str[h]; } +unsigned int hashString(const std::string& str, int h = 0) +{ + return !str.c_str()[h] ? 5381 : (hashString(str, h + 1) * 33) ^ str.c_str()[h]; +} // Assumes that for lower case comparing, the values passed in are already lower case #define Choose(x) do { std::string tttmp = x; switch (hashString(x)) { @@ -112,6 +121,7 @@ { va_list args; va_start(args, a_msg); +#ifdef _WIN32 auto sizeForFileLineAndNull = m_currentFile.size() + 64 + 1; auto sizeRequired = _vscprintf(a_msg, args) + sizeForFileLineAndNull; auto a = (char*)_alloca(sizeRequired); @@ -119,6 +129,9 @@ vsnprintf_s(a + offset, sizeRequired - offset, INT_MAX - 1, a_msg, args); OutputDebugStringA(a); _freea(a); +#else + vfprintf(stderr, a_msg, args); +#endif va_end(args); /* if (a_level == LL_Warning) @@ -141,7 +154,7 @@ } -std::string trim(std::string& str) +std::string trim(std::string str) { str.erase(0, str.find_first_not_of(" \t\r\n")); str.erase(str.find_last_not_of(" \t\r\n") + 1); @@ -149,7 +162,7 @@ } -std::string str2lower(std::string& str) +std::string str2lower(const std::string& str) { std::string ret = str; std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); @@ -157,7 +170,7 @@ } -unsigned int str2col(std::string& str) +unsigned int str2col(std::string str) { if (str[0] == '#') str = &str[1]; @@ -165,7 +178,7 @@ } -std::vector str2pointList(std::string& str) +std::vector str2pointList(const std::string& str) { std::vector ret; std::vector strs = split(str, ','); @@ -183,7 +196,7 @@ } -Point str2pos(std::string& str) +Point str2pos(const std::string& str) { Point ret = { 0, 0 }; std::vector strs = split(str, ','); @@ -193,13 +206,13 @@ } -float str2float(std::string& str) +float str2float(const std::string& str) { return (float)atof(str.c_str()); } -PenStyle str2style(std::string& str) +PenStyle str2style(const std::string& str) { Choose(str) { @@ -216,7 +229,7 @@ } -Align str2align(std::string& str) +Align str2align(const std::string& str) { unsigned int alignment = 0; std::vector strs = split(str, ','); @@ -269,6 +282,15 @@ } +#ifndef _WIN32 +int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} +#endif + + void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index cba2de9..d7ca6d2 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -15,12 +15,17 @@ class GenericTableView : public QTableView { public: - GenericTableView(GenericTableBase* a_table, QWidget* a_parent) : QTableView(a_parent), m_genericTable(a_table) - { - setAcceptDrops(m_genericTable->m_readOnly ? false : true); + GenericTableView() : QTableView(nullptr), m_genericTable(nullptr) { setDragDropMode(QAbstractItemView::InternalMove); } + void init(GenericTableBase* a_table, QWidget* a_parent) + { + setParent(a_parent); + m_genericTable = a_table; + setAcceptDrops(m_genericTable->m_readOnly ? false : true); + } + void dragEnterEvent(QDragEnterEvent *event) { if (!m_genericTable->m_readOnly) { @@ -55,8 +60,14 @@ class GenericTableUI : public QAbstractTableModel { public: - GenericTableUI(GenericTableBase* a_table, QTableView* a_view) : QAbstractTableModel(a_view), m_genericTable(a_table) + GenericTableUI() : QAbstractTableModel(nullptr), m_genericTable(nullptr), m_view(nullptr) { + } + + void init(GenericTableBase* a_table, QTableView* a_view) + { + setParent(a_view); + m_genericTable = a_table; m_view = a_view; m_view->setModel(this); m_genericTable->RegisterCallbackOnChange(updateCallback, this); diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index bdf2b44..7680de9 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -38,7 +38,8 @@ ../DocProject.h \ ../DocStyle.h \ ../DocTemplate.h \ - ../DocVisitor.h + ../DocVisitor.h \ + ../DocSVG.h SOURCES += \ ../Framework/Tests.cpp \ @@ -51,6 +52,7 @@ ../DocStyle.cpp \ ../DocTemplate.cpp \ ../DocVisitor.cpp \ + ../DocSVG.cpp \ ../main.cpp SOURCES += \ diff --git a/main.cpp b/main.cpp index 6ff651f..be7a260 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ #include "DocVisitor.h" #include "DocTemplate.h" #include "DocOutput.h" +#include "DocSVG.h" //#include "document.h" #include "html.h" @@ -25,6 +26,11 @@ #endif +#ifdef GUI_APP +# include "ui.h" +#endif + + #define DEF_IUNIT 1024 #define DEF_OUNIT 64 #define DEF_MAX_NESTING 16 @@ -108,6 +114,22 @@ } +void SVGTest(const char* a_fileName, DocOutputDevice* outputDoc) +{ + hoedown_buffer* inputBuffer = ReadInWholeFile(a_fileName); + if (!inputBuffer) + return; + // SVG xml parse + TiXmlDocument parser; + parser.Parse((char*)inputBuffer->data); + DocSVG visitor; + parser.Accept(&visitor); + visitor.DumpOperations(); + visitor.WriteTo(outputDoc); + hoedown_buffer_free(inputBuffer); +} + + void ConvertHTMLToPDF(uint8_t* inputBuffer, DocOutputDevice* outputDoc) { // xml parse @@ -119,6 +141,8 @@ parser.Parse((char*)inputBuffer); DocVisitor visitor(outputDoc, &style, &templ); parser.Accept(&visitor); + + SVGTest("test/test.svg", outputDoc); } @@ -153,480 +177,8 @@ } -#ifdef GUI_APP - -#include "ui_About.h" -#include "ui_ExtensibleObjectModelUI.h" -#include "EventLog.h" -#include "GenericTableUI.h" - - -class UiLogger : public Logger -{ -public: - EventLog* m_log; - void logMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_message) - { - m_log->LogMessage(a_location, a_message); - } -}; - - -struct UiContext -{ - Ui_MainWindow appWindow; - QMainWindow mainWindow; - - Ui_About about; - QDialog aboutDialog; - - EventLog log; - UiLogger logger; - - void setupUi() - { - logger.m_log = &log; - g_currentModule.m_messageLogger = &logger; - - appWindow.setupUi(&mainWindow); - mainWindow.menuBar()->setNativeMenuBar(false); - - about.setupUi(&aboutDialog); - aboutDialog.setFixedSize(430, 300); - - QObject::connect(appWindow.actionAbout, SIGNAL(triggered(bool)), &aboutDialog, SLOT(exec())); - - mainWindow.show(); - } -}; - - -#include - - -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; - - -struct ColorScheme -{ - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; - - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } -}; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); - - -class SchemeSelection : public QWidget -{ -public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } -}; - - - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget -{ -public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } -}; - - -#endif - - int main(int argc, char* argv[]) { -#ifdef GUI_APP - QApplication app(argc, argv); - UiContext ui; - ui.setupUi(); - - ColorPicker picker; - picker.show(); - - - runTests(); - - YQ_LOG_DEBUG("This is a test message"); - ui.log.LogMessage("blah", "hooking up ui"); - - GenericTableUI table(&ui.log, ui.appWindow.eventLog); - - ui.log.LogMessage("blah", "starting"); - ui.log.LogMessage("blah", "starting1"); - ui.log.LogMessage("blah", "starting2"); - ui.log.LogMessage("blah", "starting3"); - ui.log.LogMessage("blah", "starting4"); - ui.log.LogMessage("blah", "starting"); - ui.log.LogMessage("blah", "true"); - ui.log.LogMessage("false", "starting"); - - return app.exec(); -#else DocOutputDevice outputDoc; Context context = { "Test Markdown to PDF", @@ -636,7 +188,16 @@ &outputDoc }; ProcessConversionContext(&context); - return 0; + +#ifdef GUI_APP + QApplication app(argc, argv); + UiContext ui; + ui.setupUi(); + YQ_LOG_DEBUG("This is a test message"); + runTests(); + app.exec(); #endif + + return 0; } diff --git a/ui.h b/ui.h new file mode 100644 index 0000000..2074e52 --- /dev/null +++ b/ui.h @@ -0,0 +1,60 @@ +#include "ui_About.h" +#include "ui_ExtensibleObjectModelUI.h" +#include "EventLog.h" +#include "GenericTableUI.h" +#include "ColorPicker.h" + + +class UiLogger : public Logger +{ +public: + void init(EventLog* a_log) + { + m_log = a_log; + } + void logMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_message) + { + m_log->LogMessage(a_location, a_message); + } +private: + EventLog* m_log; +}; + + +struct UiContext +{ + Ui_MainWindow appWindow; + QMainWindow mainWindow; + + Ui_About about; + QDialog aboutDialog; + + EventLog log; + UiLogger logger; + GenericTableUI logView; + + ColorPicker picker; + + void setupUi() + { + logger.init(&log); + g_currentModule.m_messageLogger = &logger; + + appWindow.setupUi(&mainWindow); + mainWindow.menuBar()->setNativeMenuBar(false); + + about.setupUi(&aboutDialog); + aboutDialog.setFixedSize(430, 300); + + QObject::connect(appWindow.actionAbout, SIGNAL(triggered(bool)), &aboutDialog, SLOT(exec())); + + mainWindow.show(); + picker.show(); + + log.LogMessage("blah", "hooking up ui"); + logView.init(&log, appWindow.eventLog); + log.LogMessage("blah", "starting"); + } +}; + +