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());
115 newFilePath =
QString(
"%1/temp-%2.%3")
116 .
arg(finfo.canonicalPath())
117 .
arg(uniqueString(12))
118 .
arg(finfo.suffix());
122 b->setFileName(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)
587QRgb BitmapImage::pixel(
int x,
int y)
589 return pixel(
QPoint(x, y));
592QRgb BitmapImage::pixel(
QPoint p)
594 QRgb result = qRgba(0, 0, 0, 0);
600void BitmapImage::setPixel(
int x,
int y, QRgb color)
602 setPixel(
QPoint(x, y), color);
605void BitmapImage::setPixel(
QPoint p, QRgb color)
615void BitmapImage::fillNonAlphaPixels(
const QRgb color)
617 if (mBounds.
isEmpty()) {
return; }
625 int width = 2 + pen.
width();
627 if (!image()->isNull())
641 int width = pen.
width();
667 int width = pen.
width();
692 int width = pen.
width();
697 if (!image()->isNull())
736BitmapImage* BitmapImage::scanToTransparent(
BitmapImage *img,
const int threshold,
const bool redEnabled,
const bool greenEnabled,
const bool blueEnabled)
738 Q_ASSERT(img !=
nullptr);
740 QRgb rgba = img->constScanLine(img->left(), img->top());
741 if (qAlpha(rgba) == 0)
744 for (
int x = img->left(); x <= img->right(); x++)
746 for (
int y = img->top(); y <= img->bottom(); y++)
748 rgba = img->constScanLine(x, y);
750 if (qAlpha(rgba) == 0)
753 const int grayValue = qGray(rgba);
754 const int redValue = qRed(rgba);
755 const int greenValue = qGreen(rgba);
756 const int blueValue = qBlue(rgba);
757 if (grayValue >= threshold)
759 img->scanLine(x, y, transp);
761 else if (redValue > greenValue + COLORDIFF &&
762 redValue > blueValue + COLORDIFF &&
763 redValue > grayValue + GRAYSCALEDIFF)
767 img->scanLine(x, y, redline);
771 img->scanLine(x, y, transp);
774 else if (greenValue > redValue + COLORDIFF &&
775 greenValue > blueValue + COLORDIFF &&
776 greenValue > grayValue + GRAYSCALEDIFF)
780 img->scanLine(x, y, greenline);
784 img->scanLine(x, y, transp);
787 else if (blueValue > redValue + COLORDIFF &&
788 blueValue > greenValue + COLORDIFF &&
789 blueValue > grayValue + GRAYSCALEDIFF)
793 img->scanLine(x, y, blueline);
797 img->scanLine(x, y, transp);
802 if (grayValue >= LOW_THRESHOLD)
804 const qreal factor =
static_cast<qreal
>(threshold - grayValue) /
static_cast<qreal
>(threshold - LOW_THRESHOLD);
805 img->scanLine(x , y, qRgba(0, 0, 0,
static_cast<int>(threshold * factor)));
809 img->scanLine(x , y, blackline);
822 bool b = mImage.
save(filename);
823 return (b) ? Status::OK : Status::FAIL;
826 if (bounds().isEmpty())
844void BitmapImage::clear()
847 mBounds =
QRect(0, 0, 0, 0);
852QRgb BitmapImage::constScanLine(
int x,
int y)
const
854 QRgb result = QRgb();
856 result = *(
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(y - mBounds.
top())) + x - mBounds.
left());
861void BitmapImage::scanLine(
int x,
int y, QRgb color)
867 *(
reinterpret_cast<QRgb*
>(image()->
scanLine(y - mBounds.
top())) + x - mBounds.
left()) = color;
870void BitmapImage::clear(
QRect rectangle)
885bool BitmapImage::floodFill(
BitmapImage** replaceImage,
887 const QRect& cameraRect,
889 const QRgb& fillColor,
891 const int expandValue)
894 const QRect& fillBounds = targetImage->mBounds.
adjusted(-1, -1, 1, 1);
895 QRect maxBounds = cameraRect.
united(fillBounds).
adjusted(-expandValue, -expandValue, expandValue, expandValue);
896 const int maxWidth = maxBounds.
width(), left = maxBounds.
left(), top = maxBounds.
top();
899 tolerance =
static_cast<int>(qPow(tolerance, 2));
902 bool *filledPixels = floodFillPoints(targetImage, maxBounds, point, tolerance, newBounds);
907 const QRect& expandRect = newBounds.
adjusted(-expandValue, -expandValue, expandValue, expandValue);
908 if (expandValue > 0) {
909 newBounds = expandRect;
911 if (!maxBounds.
contains(newBounds)) {
912 newBounds = maxBounds;
916 if (expandValue > 0) {
917 expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
923 for (
int y = translatedSearchBounds.
top(); y <= translatedSearchBounds.
bottom(); y++)
925 for (
int x = translatedSearchBounds.
left(); x <= translatedSearchBounds.
right(); x++)
927 const int index = y * maxWidth + x;
928 if (!filledPixels[index])
932 (*replaceImage)->scanLine(x + left, y + top, fillColor);
936 delete[] filledPixels;
943bool* BitmapImage::floodFillPoints(
const BitmapImage* targetImage,
944 const QRect& searchBounds,
949 QRgb oldColor = targetImage->constScanLine(point.
x(), point.
y());
950 oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
959 bool spanLeft =
false;
960 bool spanRight =
false;
965 bool *filledPixels =
new bool[searchBounds.
height()*searchBounds.
width()]{};
968 while (!queue.
empty())
972 point.
setX(tempPoint.
x());
973 point.
setY(tempPoint.
y());
977 int xCoord = xTemp - searchBounds.
left();
978 int yCoord = point.
y() - searchBounds.
top();
980 if (filledPixels[yCoord*searchBounds.
width()+xCoord])
continue;
982 while (xTemp >= searchBounds.
left() &&
983 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data())) xTemp--;
986 spanLeft = spanRight =
false;
987 while (xTemp <= searchBounds.
right() &&
988 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data()))
992 if (!blitBounds.contains(floodPoint)) {
993 blitBounds.extend(floodPoint);
996 xCoord = xTemp - searchBounds.
left();
998 filledPixels[yCoord*searchBounds.
width()+xCoord] =
true;
1000 if (!spanLeft && (point.
y() > searchBounds.
top()) &&
1001 compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
1005 else if (spanLeft && (point.
y() > searchBounds.
top()) &&
1006 !
compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
1010 if (!spanRight && point.
y() < searchBounds.
bottom() &&
1011 compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
1015 else if (spanRight && point.
y() < searchBounds.
bottom() &&
1016 !
compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
1025 newBounds = blitBounds;
1027 return filledPixels;
1047 const int maxWidth = maxBounds.
width();
1048 const int length = maxBounds.
height() * maxBounds.
width();
1050 int* manhattanPoints =
new int[length]{};
1053 std::fill_n(manhattanPoints, length, searchBounds.
width()+searchBounds.
height());
1055 for (
int y = searchBounds.
top(); y <= searchBounds.
bottom(); y++)
1057 for (
int x = searchBounds.
left(); x <= searchBounds.
right(); x++)
1059 const int index = y*maxWidth+x;
1061 if (fillPixels[index]) {
1062 manhattanPoints[index] = 0;
1066 if (y > searchBounds.
top()) {
1068 manhattanPoints[index] = qMin(manhattanPoints[index],
1069 manhattanPoints[(y - 1) * maxWidth+x] + 1);
1071 int distance = manhattanPoints[index];
1072 if (distance <= expand) {
1073 fillPixels[index] =
true;
1076 if (x > searchBounds.
left()) {
1078 manhattanPoints[index] = qMin(manhattanPoints[index],
1079 manhattanPoints[y*maxWidth+(x - 1)] + 1);
1081 int distance = manhattanPoints[index];
1082 if (distance <= expand) {
1083 fillPixels[index] =
true;
1090 for (
int y = searchBounds.
bottom(); y >= searchBounds.
top(); y--)
1092 for (
int x = searchBounds.
right(); x >= searchBounds.
left(); x--)
1094 const int index = y*maxWidth+x;
1096 if (y + 1 < searchBounds.
bottom()) {
1097 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[(y + 1)*maxWidth+x] + 1);
1099 int distance = manhattanPoints[index];
1100 if (distance <= expand) {
1101 fillPixels[index] =
true;
1104 if (x + 1 < searchBounds.
right()) {
1105 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[y*maxWidth+(x + 1)] + 1);
1107 int distance = manhattanPoints[index];
1108 if (distance <= expand) {
1109 fillPixels[index] =
true;
1115 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
bool copy(const QString &newName)
bool exists() 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