17#include "bitmapimage.h"
24#include <QPainterPath>
29#include "tiledbuffer.h"
31BitmapImage::BitmapImage()
39 mEnableAutoCrop = a.mEnableAutoCrop;
40 mOpacity = a.mOpacity;
44BitmapImage::BitmapImage(
const QRect& rectangle,
const QColor& color)
52BitmapImage::BitmapImage(
const QPoint& topLeft,
const QImage& image)
59BitmapImage::BitmapImage(
const QPoint& topLeft,
const QString& path)
69BitmapImage::~BitmapImage()
73void BitmapImage::setImage(
QImage* img)
89 KeyFrame::operator=(a);
92 mOpacity = a.mOpacity;
103 const bool validKeyFrame = !fileName().
isEmpty();
104 if (validKeyFrame && !isModified())
109 Q_ASSERT(finfo.isAbsolute());
110 Q_ASSERT(QFile::exists(fileName()));
115 newFilePath =
QString(
"%1/temp-%2.%3")
116 .
arg(finfo.canonicalPath())
117 .
arg(uniqueString(12))
118 .
arg(finfo.suffix());
120 while (QFile::exists(newFilePath));
122 b->setFileName(newFilePath);
123 bool ok = QFile::copy(fileName(), newFilePath);
125 qDebug() <<
"COPY>" << fileName();
130void BitmapImage::loadFile()
132 if (!fileName().isEmpty() && !isLoaded())
140void BitmapImage::unloadFile()
142 if (isModified() ==
false)
148bool BitmapImage::isLoaded()
const
153quint64 BitmapImage::memoryUsage()
157 return imageSize(mImage);
162void BitmapImage::paintImage(
QPainter& painter)
174QImage* BitmapImage::image()
197 if(bitmapImage->width() <= 0 || bitmapImage->height() <= 0)
204 QImage* image2 = bitmapImage->image();
216 if(tiledBuffer->bounds().
width() <= 0 || tiledBuffer->bounds().
height() <= 0)
220 extend(tiledBuffer->bounds());
225 auto const tiles = tiledBuffer->tiles();
226 for (
const Tile* item : tiles) {
227 const QPixmap& tilePixmap = item->pixmap();
228 const QPoint& tilePos = item->pos();
236void BitmapImage::moveTopLeft(
QPoint point)
243void BitmapImage::transform(
QRect newBoundaries,
bool smoothTransform)
245 mBounds = newBoundaries;
254 painter.
drawImage(newBoundaries, *image());
263 Q_ASSERT(!selection.
isEmpty());
275 transformedImage = selectedPart.image()->
transformed(transform);
280BitmapImage BitmapImage::transformed(
QRect newBoundaries,
bool smoothTransform)
283 QPainter painter(transformedImage.image());
286 painter.
drawImage(newBoundaries, *image());
288 return transformedImage;
301 if (mBounds == newBoundaries)
return;
312 mBounds = newBoundaries;
318void BitmapImage::extend(
const QPoint &p)
326void BitmapImage::extend(
QRect rectangle)
339 if (!newImage.isNull())
346 mBounds = newBoundaries;
395 newBoundaries = mBounds;
404 newBoundaries = mBounds;
411 newBoundaries = mBounds.
united(sourceBounds);
433 if (!mEnableAutoCrop)
return;
435 if (mImage.
isNull())
return;
437 Q_ASSERT(mBounds.
size() == mImage.
size());
443 const int width = mImage.
width();
447 int relBottom = mBounds.
height() - 1;
451 while (isEmpty && relTop <= relBottom)
454 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop));
455 for (
int col = 0; col < width; col++)
459 if (qAlpha(*cursor) != 0)
479 while (isEmpty && relBottom >= relTop)
482 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relBottom));
483 for (
int col = 0; col < width; col++)
487 if(qAlpha(*cursor) != 0)
507 int relRight = mBounds.
width()-1;
510 isEmpty = (relBottom >= relTop);
511 while (isEmpty && relBottom >= relTop && relLeft <= relRight)
514 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop)) + relLeft;
520 for (
int row = relTop; row <= relBottom; row++)
524 if(qAlpha(*cursor) != 0)
544 isEmpty = (relBottom >= relTop);
545 while (isEmpty && relRight >= relLeft)
548 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop)) + relRight;
554 for (
int row = relTop; row <= relBottom; row++)
558 if(qAlpha(*cursor) != 0)
577 if (relTop > relBottom || relLeft > relRight) {
591QRgb BitmapImage::pixel(
int x,
int y)
593 return pixel(
QPoint(x, y));
596QRgb BitmapImage::pixel(
QPoint p)
598 QRgb result = qRgba(0, 0, 0, 0);
604void BitmapImage::setPixel(
int x,
int y, QRgb color)
606 setPixel(
QPoint(x, y), color);
609void BitmapImage::setPixel(
QPoint p, QRgb color)
619void BitmapImage::fillNonAlphaPixels(
const QRgb color)
621 if (mBounds.
isEmpty()) {
return; }
629 int width = 2 + pen.
width();
631 if (!image()->isNull())
645 int width = pen.
width();
671 int width = pen.
width();
696 int width = pen.
width();
701 if (!image()->isNull())
740BitmapImage* BitmapImage::scanToTransparent(
BitmapImage *img,
const int threshold,
const bool redEnabled,
const bool greenEnabled,
const bool blueEnabled)
742 Q_ASSERT(img !=
nullptr);
744 QRgb rgba = img->constScanLine(img->left(), img->top());
745 if (qAlpha(rgba) == 0)
748 for (
int x = img->left(); x <= img->right(); x++)
750 for (
int y = img->top(); y <= img->bottom(); y++)
752 rgba = img->constScanLine(x, y);
754 if (qAlpha(rgba) == 0)
757 const int grayValue = qGray(rgba);
758 const int redValue = qRed(rgba);
759 const int greenValue = qGreen(rgba);
760 const int blueValue = qBlue(rgba);
761 if (grayValue >= threshold)
763 img->scanLine(x, y, transp);
765 else if (redValue > greenValue + COLORDIFF &&
766 redValue > blueValue + COLORDIFF &&
767 redValue > grayValue + GRAYSCALEDIFF)
771 img->scanLine(x, y, redline);
775 img->scanLine(x, y, transp);
778 else if (greenValue > redValue + COLORDIFF &&
779 greenValue > blueValue + COLORDIFF &&
780 greenValue > grayValue + GRAYSCALEDIFF)
784 img->scanLine(x, y, greenline);
788 img->scanLine(x, y, transp);
791 else if (blueValue > redValue + COLORDIFF &&
792 blueValue > greenValue + COLORDIFF &&
793 blueValue > grayValue + GRAYSCALEDIFF)
797 img->scanLine(x, y, blueline);
801 img->scanLine(x, y, transp);
806 if (grayValue >= LOW_THRESHOLD)
808 const qreal factor =
static_cast<qreal
>(threshold - grayValue) /
static_cast<qreal
>(threshold - LOW_THRESHOLD);
809 img->scanLine(x , y, qRgba(0, 0, 0,
static_cast<int>(threshold * factor)));
813 img->scanLine(x , y, blackline);
826 bool b = mImage.
save(filename);
827 return (b) ? Status::OK : Status::FAIL;
830 if (bounds().isEmpty())
848void BitmapImage::clear()
851 mBounds =
QRect(0, 0, 0, 0);
856QRgb BitmapImage::constScanLine(
int x,
int y)
const
858 QRgb result = QRgb();
860 result = *(
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(y - mBounds.
top())) + x - mBounds.
left());
865void BitmapImage::scanLine(
int x,
int y, QRgb color)
871 *(
reinterpret_cast<QRgb*
>(image()->
scanLine(y - mBounds.
top())) + x - mBounds.
left()) = color;
874void BitmapImage::clear(
QRect rectangle)
889bool BitmapImage::floodFill(
BitmapImage** replaceImage,
891 const QRect& cameraRect,
893 const QRgb& fillColor,
895 const int expandValue)
898 const QRect& fillBounds = targetImage->mBounds.
adjusted(-1, -1, 1, 1);
899 QRect maxBounds = cameraRect.
united(fillBounds).
adjusted(-expandValue, -expandValue, expandValue, expandValue);
900 const int maxWidth = maxBounds.
width(), left = maxBounds.
left(), top = maxBounds.
top();
903 tolerance =
static_cast<int>(qPow(tolerance, 2));
906 bool *filledPixels = floodFillPoints(targetImage, maxBounds, point, tolerance, newBounds);
911 const QRect& expandRect = newBounds.
adjusted(-expandValue, -expandValue, expandValue, expandValue);
912 if (expandValue > 0) {
913 newBounds = expandRect;
915 if (!maxBounds.
contains(newBounds)) {
916 newBounds = maxBounds;
920 if (expandValue > 0) {
921 expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
927 for (
int y = translatedSearchBounds.
top(); y <= translatedSearchBounds.
bottom(); y++)
929 for (
int x = translatedSearchBounds.
left(); x <= translatedSearchBounds.
right(); x++)
931 const int index = y * maxWidth + x;
932 if (!filledPixels[index])
936 (*replaceImage)->scanLine(x + left, y + top, fillColor);
940 delete[] filledPixels;
947bool* BitmapImage::floodFillPoints(
const BitmapImage* targetImage,
948 const QRect& searchBounds,
953 QRgb oldColor = targetImage->constScanLine(point.
x(), point.
y());
954 oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
963 bool spanLeft =
false;
964 bool spanRight =
false;
969 bool *filledPixels =
new bool[searchBounds.
height()*searchBounds.
width()]{};
972 while (!queue.
empty())
976 point.
setX(tempPoint.
x());
977 point.
setY(tempPoint.
y());
981 int xCoord = xTemp - searchBounds.
left();
982 int yCoord = point.
y() - searchBounds.
top();
984 if (filledPixels[yCoord*searchBounds.
width()+xCoord])
continue;
986 while (xTemp >= searchBounds.
left() &&
987 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data())) xTemp--;
990 spanLeft = spanRight =
false;
991 while (xTemp <= searchBounds.
right() &&
992 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data()))
996 if (!blitBounds.contains(floodPoint)) {
997 blitBounds.extend(floodPoint);
1000 xCoord = xTemp - searchBounds.
left();
1002 filledPixels[yCoord*searchBounds.
width()+xCoord] =
true;
1004 if (!spanLeft && (point.
y() > searchBounds.
top()) &&
1005 compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
1009 else if (spanLeft && (point.
y() > searchBounds.
top()) &&
1010 !
compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
1014 if (!spanRight && point.
y() < searchBounds.
bottom() &&
1015 compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
1019 else if (spanRight && point.
y() < searchBounds.
bottom() &&
1020 !
compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
1029 newBounds = blitBounds;
1031 return filledPixels;
1051 const int maxWidth = maxBounds.
width();
1052 const int length = maxBounds.
height() * maxBounds.
width();
1054 int* manhattanPoints =
new int[length]{};
1057 std::fill_n(manhattanPoints, length, searchBounds.
width()+searchBounds.
height());
1059 for (
int y = searchBounds.
top(); y <= searchBounds.
bottom(); y++)
1061 for (
int x = searchBounds.
left(); x <= searchBounds.
right(); x++)
1063 const int index = y*maxWidth+x;
1065 if (fillPixels[index]) {
1066 manhattanPoints[index] = 0;
1070 if (y > searchBounds.
top()) {
1072 manhattanPoints[index] = qMin(manhattanPoints[index],
1073 manhattanPoints[(y - 1) * maxWidth+x] + 1);
1075 int distance = manhattanPoints[index];
1076 if (distance <= expand) {
1077 fillPixels[index] =
true;
1080 if (x > searchBounds.
left()) {
1082 manhattanPoints[index] = qMin(manhattanPoints[index],
1083 manhattanPoints[y*maxWidth+(x - 1)] + 1);
1085 int distance = manhattanPoints[index];
1086 if (distance <= expand) {
1087 fillPixels[index] =
true;
1094 for (
int y = searchBounds.
bottom(); y >= searchBounds.
top(); y--)
1096 for (
int x = searchBounds.
right(); x >= searchBounds.
left(); x--)
1098 const int index = y*maxWidth+x;
1100 if (y + 1 < searchBounds.
bottom()) {
1101 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[(y + 1)*maxWidth+x] + 1);
1103 int distance = manhattanPoints[index];
1104 if (distance <= expand) {
1105 fillPixels[index] =
true;
1108 if (x + 1 < searchBounds.
right()) {
1109 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[y*maxWidth+(x + 1)] + 1);
1111 int distance = manhattanPoints[index];
1112 if (distance <= expand) {
1113 fillPixels[index] =
true;
1119 delete[] manhattanPoints;
void updateBounds(QRect rectangle)
Update image bounds.
void setCompositionModeBounds(BitmapImage *source, QPainter::CompositionMode cm)
Updates the bounds after a drawImage operation with the composition mode cm.
static void expandFill(bool *fillPixels, const QRect &searchBounds, const QRect &maxBounds, int expand)
Finds all pixels closest to the input color and applies the input color to the image.
void autoCrop()
Removes any transparent borders by reducing the boundaries.
static bool compareColor(QRgb newColor, QRgb oldColor, int tolerance, QHash< QRgb, bool > *cache)
Compare colors for the purposes of flood filling.
const QGradient * gradient() const const
Qt::BrushStyle style() const const
const uchar * constScanLine(int i) const const
void fill(uint pixelValue)
bool isNull() const const
QRgb pixel(int x, int y) const const
bool save(const QString &fileName, const char *format, int quality) const const
void setPixel(int x, int y, uint index_or_rgb)
void append(const T &value)
int count(const T &value) const const
void drawEllipse(const QRectF &rectangle)
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags)
void drawLine(const QLineF &line)
void drawPath(const QPainterPath &path)
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void drawPoint(const QPointF &position)
void drawRect(const QRectF &rectangle)
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setBrush(const QBrush &brush)
void setCompositionMode(QPainter::CompositionMode mode)
void setPen(const QColor &color)
void setRenderHint(QPainter::RenderHint hint, bool on)
void setWorldMatrixEnabled(bool enable)
QRectF controlPointRect() const const
QPainterPath::Element elementAt(int index) const const
qreal length() const const
QPoint toPoint() const const
QPointF center() const const
QPointF focalPoint() const const
void setCenter(const QPointF ¢er)
void setFocalPoint(const QPointF &focalPoint)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
bool contains(const QRect &rectangle, bool proper) const const
QRect intersected(const QRect &rectangle) const const
bool isEmpty() const const
void moveTopLeft(const QPoint &position)
QRect normalized() const const
void setHeight(int height)
void setSize(const QSize &size)
QPoint topLeft() const const
QRect translated(int dx, int dy) const const
QRect united(const QRect &rectangle) const const
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
QRect toRect() const const
QRectF translated(qreal dx, qreal dy) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
bool isEmpty() const const