Skip to content

Instantly share code, notes, and snippets.

@Glavak
Created February 22, 2018 17:24
Show Gist options
  • Save Glavak/69400a15afff6dc646a7ca7cb99a9926 to your computer and use it in GitHub Desktop.
Save Glavak/69400a15afff6dc646a7ca7cb99a9926 to your computer and use it in GitHub Desktop.
#include "hexpoint.h"
#include "myimage.h"
#include <QDebug>
#include <QStack>
MyImage::MyImage(int width, int height) : QImage(width, height, QImage::Format_ARGB32)
{
clear(Color(240, 240, 240));
}
bool MyImage::isBlackPixel(QPoint coords)
{
return *((QRgb *) getPixel(coords)) == qRgb(0, 0, 0);
}
void MyImage::clear(Color color)
{
QRgb rgb = qRgb(color.r, color.g, color.b);
std::fill(this->bits(), this->bits() + this->bytesPerLine()*this->height(), rgb);
}
void MyImage::drawHex(QPointF center, QPoint offset, float size, int borderThickness, Color border, Color fill)
{
float hexWidthHalf = sqrt(3) * size / 2;
if (center.x() < -hexWidthHalf || center.y() < -size) return;
if (center.x() > width() + hexWidthHalf || center.y() > height() + size) return;
// Left
QPointF start = center + QPointF(-hexWidthHalf, -size/2);
QPointF end = center + QPointF(-hexWidthHalf, size/2);
drawLine(start.toPoint(), end.toPoint(), borderThickness, border);
// Bottom-left:
start = center + QPointF(0, size);
drawLine(start.toPoint(), end.toPoint(), borderThickness, border);
// Bottom-right:
end = center + QPointF(hexWidthHalf, size/2);
drawLine(start.toPoint(), end.toPoint(), borderThickness, border);
// Right
start = center + QPointF(hexWidthHalf, -size/2);
drawLine(start.toPoint(), end.toPoint(), borderThickness, border);
// Top-right
end = center + QPointF(0, -size);
drawLine(start.toPoint(), end.toPoint(), borderThickness, border);
// Top-left
start = center + QPointF(-hexWidthHalf, -size/2);
drawLine(start.toPoint(), end.toPoint(), borderThickness, border);
QPoint fillPoint = center.toPoint();
if (fillPoint.x() < 0) fillPoint.rx() = 0;
if (fillPoint.y() < 0) fillPoint.ry() = 0;
if (fillPoint.x() >= width()) fillPoint.rx() = width() - 1;
if (fillPoint.y() >= height()) fillPoint.ry() = height() - 1;
if (HexPoint::fromPixel(fillPoint-offset, size, borderThickness) == HexPoint::fromPixel(center-offset, size, borderThickness))
{
fillArea(fillPoint, fill);
}
}
void MyImage::drawLine(QPoint start, QPoint end, int thickness, Color color)
{
bool steep = abs(end.y() - start.y()) > abs(end.x() - start.x());
if (steep)
{
// Swap x and y
std::swap(start.rx(), start.ry());
std::swap(end.rx(), end.ry());
}
if (start.x() > end.x())
{
// Swap start and end
std::swap(start.rx(), end.rx());
std::swap(start.ry(), end.ry());
}
int dx = end.x() - start.x();
int dy = abs(end.y() - start.y());
int error = dx / 2;
int ystep = (start.y() < end.y()) ? 1 : -1;
int y = start.y();
for (int x = start.x(); x <= end.x(); x++)
{
QPoint point(steep ? y : x, steep ? x : y);
if (x >= 0 && x < (steep ? height() : width()))
{
if (y >= 0 && y < (steep ? width() : height()))
drawPixelUnsafe(point, color);
int currentThickness = 1;
while (currentThickness < thickness)
{
int & orthCoord = (steep ? point.rx() : point.ry());
orthCoord += currentThickness;
if (orthCoord >= 0 && orthCoord < (steep ? width() : height()))
drawPixelUnsafe(point, color);
if (++currentThickness >= thickness) break;
orthCoord -= currentThickness;
if (orthCoord >= 0 && orthCoord < (steep ? width() : height()))
drawPixelUnsafe(point, color);
}
}
error -= dy;
if (error < 0)
{
y += ystep;
error += dx;
}
}
}
void MyImage::drawPixel(QPoint coords, Color color)
{
if (!this->isPointInside(coords))
{
//qDebug() << "Pixel: out of range: (" << coords.x() << ", " << coords.y() << ");";
return;
}
this->drawPixelUnsafe(coords, color);
}
void MyImage::fillArea(QPoint seed, Color color)
{
if (!this->isPointInside(seed))
{
//qDebug() << "Fill: out of range: (" << coords.x() << ", " << coords.y() << ");";
return;
}
QRgb seedColor = this->getPixelColor(seed);
QRgb targetColor = qRgb(color.r, color.g, color.b);
if (seedColor == targetColor)
{
// Same color as already
return;
}
Span span(seed.x(), seed.x(), seed.y());
while (span.xStart > 0 &&
seedColor == getPixelColor(QPoint(span.xStart - 1, seed.y())))
{
span.xStart--;
}
while (span.xEnd < this->width() - 1 &&
seedColor == getPixelColor(QPoint(span.xEnd + 1, seed.y())))
{
span.xEnd++;
}
QStack<Span> spans;
spans.push(span);
while (!spans.isEmpty())
{
span = spans.pop();
Span newSpanTop(-1, -1, span.y - 1);
Span newSpanBottom(-1, -1, span.y + 1);
for (int x = span.xStart; x <= span.xEnd; x++)
{
// Check for new top span:
if (newSpanTop.xStart == -1 &&
newSpanTop.y >= 0 &&
seedColor == getPixelColor(QPoint(x, span.y-1)))
{
newSpanTop.xStart = x;
}
if (newSpanTop.xStart != -1 &&
newSpanTop.y >= 0 &&
(x == this->width() - 1 ||
seedColor != getPixelColor(QPoint(x+1, span.y-1))))
{
newSpanTop.xEnd = x;
spans.push(newSpanTop);
newSpanTop.xStart = newSpanTop.xEnd = -1;
}
// Check for new bottom span:
if (newSpanBottom.xStart == -1 &&
newSpanBottom.y < this->height() &&
seedColor == getPixelColor(QPoint(x, span.y+1)))
{
newSpanBottom.xStart = x;
}
if (newSpanBottom.xStart != -1 &&
newSpanBottom.y < this->height() &&
(x == this->width() - 1 ||
seedColor != getPixelColor(QPoint(x+1, span.y+1))))
{
newSpanBottom.xEnd = x;
spans.push(newSpanBottom);
newSpanBottom.xStart = newSpanBottom.xEnd = -1;
}
// Fill pixel:
quint8 * pix = this->getPixel(QPoint(x, span.y));
((QRgb *) pix)[0] = targetColor;
}
}
}
bool MyImage::isPointInside(QPoint point) const
{
if (point.x() < 0 || point.y() < 0 ||
point.x() >= this->width() || point.y() >= this->height())
{
return false;
}
return true;
}
quint8 * MyImage::getPixel(QPoint coords)
{
quint8 * pix = this->bits();
pix += this->bytesPerLine() * coords.y();
pix += coords.x() * 4;
return pix;
}
QRgb MyImage::getPixelColor(QPoint coords)
{
return *((QRgb *) this->getPixel(coords));
}
void MyImage::drawPixelUnsafe(QPoint coords, Color color)
{
quint8 * pix = this->getPixel(coords);
((QRgb *) pix)[0] = qRgb(color.r, color.g, color.b);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment