17#include "bitmapimage.h"
23#include <QPainterPath>
28#include "tiledbuffer.h"
30BitmapImage::BitmapImage()
38 mEnableAutoCrop = a.mEnableAutoCrop;
39 mOpacity = a.mOpacity;
43BitmapImage::BitmapImage(
const QRect& rectangle,
const QColor& color)
51BitmapImage::BitmapImage(
const QPoint& topLeft,
const QImage& image)
58BitmapImage::BitmapImage(
const QPoint& topLeft,
const QString& path)
68BitmapImage::~BitmapImage()
72void BitmapImage::setImage(
QImage* img)
88 KeyFrame::operator=(a);
91 mOpacity = a.mOpacity;
102 const bool validKeyFrame = !fileName().
isEmpty();
103 if (validKeyFrame && !isLoaded())
108 Q_ASSERT(finfo.isAbsolute());
112 .
arg(finfo.canonicalPath())
113 .
arg(finfo.completeBaseName())
114 .
arg(uniqueString(12))
115 .
arg(finfo.suffix());
116 b->setFileName(newFileName);
120 qDebug() <<
"COPY>" << fileName();
125void BitmapImage::loadFile()
127 if (!fileName().isEmpty() && !isLoaded())
135void BitmapImage::unloadFile()
137 if (isModified() ==
false)
143bool BitmapImage::isLoaded()
const
148quint64 BitmapImage::memoryUsage()
152 return imageSize(mImage);
157void BitmapImage::paintImage(
QPainter& painter)
169QImage* BitmapImage::image()
192 if(bitmapImage->width() <= 0 || bitmapImage->height() <= 0)
199 QImage* image2 = bitmapImage->image();
211 if(tiledBuffer->bounds().
width() <= 0 || tiledBuffer->bounds().
height() <= 0)
215 extend(tiledBuffer->bounds());
220 auto const tiles = tiledBuffer->tiles();
221 for (
const Tile* item : tiles) {
222 const QPixmap& tilePixmap = item->pixmap();
223 const QPoint& tilePos = item->pos();
231void BitmapImage::moveTopLeft(
QPoint point)
238void BitmapImage::transform(
QRect newBoundaries,
bool smoothTransform)
240 mBounds = newBoundaries;
249 painter.
drawImage(newBoundaries, *image());
258 Q_ASSERT(!selection.
isEmpty());
270 transformedImage = selectedPart.image()->
transformed(transform);
275BitmapImage BitmapImage::transformed(
QRect newBoundaries,
bool smoothTransform)
278 QPainter painter(transformedImage.image());
281 painter.
drawImage(newBoundaries, *image());
283 return transformedImage;
296 if (mBounds == newBoundaries)
return;
307 mBounds = newBoundaries;
313void BitmapImage::extend(
const QPoint &p)
321void BitmapImage::extend(
QRect rectangle)
334 if (!newImage.isNull())
341 mBounds = newBoundaries;
390 newBoundaries = mBounds;
399 newBoundaries = mBounds;
406 newBoundaries = mBounds.
united(sourceBounds);
428 if (!mEnableAutoCrop)
return;
430 if (mImage.
isNull())
return;
432 Q_ASSERT(mBounds.
size() == mImage.
size());
438 const int width = mImage.
width();
442 int relBottom = mBounds.
height() - 1;
446 while (isEmpty && relTop <= relBottom)
449 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop));
450 for (
int col = 0; col < width; col++)
454 if (qAlpha(*cursor) != 0)
474 while (isEmpty && relBottom >= relTop)
477 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relBottom));
478 for (
int col = 0; col < width; col++)
482 if(qAlpha(*cursor) != 0)
502 int relRight = mBounds.
width()-1;
505 isEmpty = (relBottom >= relTop);
506 while (isEmpty && relBottom >= relTop && relLeft <= relRight)
509 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop)) + relLeft;
515 for (
int row = relTop; row <= relBottom; row++)
519 if(qAlpha(*cursor) != 0)
539 isEmpty = (relBottom >= relTop);
540 while (isEmpty && relRight >= relLeft)
543 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop)) + relRight;
549 for (
int row = relTop; row <= relBottom; row++)
553 if(qAlpha(*cursor) != 0)
582QRgb BitmapImage::pixel(
int x,
int y)
584 return pixel(
QPoint(x, y));
587QRgb BitmapImage::pixel(
QPoint p)
589 QRgb result = qRgba(0, 0, 0, 0);
595void BitmapImage::setPixel(
int x,
int y, QRgb color)
597 setPixel(
QPoint(x, y), color);
600void BitmapImage::setPixel(
QPoint p, QRgb color)
610void BitmapImage::fillNonAlphaPixels(
const QRgb color)
612 if (mBounds.
isEmpty()) {
return; }
620 int width = 2 + pen.
width();
622 if (!image()->isNull())
636 int width = pen.
width();
662 int width = pen.
width();
687 int width = pen.
width();
692 if (!image()->isNull())
735 bool b = mImage.
save(filename);
736 return (b) ? Status::OK : Status::FAIL;
739 if (bounds().isEmpty())
745 return (b) ? Status::OK : Status::FAIL;
752void BitmapImage::clear()
755 mBounds =
QRect(0, 0, 0, 0);
760QRgb BitmapImage::constScanLine(
int x,
int y)
const
762 QRgb result = QRgb();
764 result = *(
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(y - mBounds.
top())) + x - mBounds.
left());
769void BitmapImage::scanLine(
int x,
int y, QRgb color)
775 *(
reinterpret_cast<QRgb*
>(image()->
scanLine(y - mBounds.
top())) + x - mBounds.
left()) = color;
778void BitmapImage::clear(
QRect rectangle)
793bool BitmapImage::floodFill(
BitmapImage** replaceImage,
795 const QRect& cameraRect,
797 const QRgb& fillColor,
799 const int expandValue)
802 const QRect& fillBounds = targetImage->mBounds.
adjusted(-1, -1, 1, 1);
803 QRect maxBounds = cameraRect.
united(fillBounds).
adjusted(-expandValue, -expandValue, expandValue, expandValue);
804 const int maxWidth = maxBounds.
width(), left = maxBounds.
left(), top = maxBounds.
top();
807 tolerance =
static_cast<int>(qPow(tolerance, 2));
810 bool *filledPixels = floodFillPoints(targetImage, maxBounds, point, tolerance, newBounds);
815 const QRect& expandRect = newBounds.
adjusted(-expandValue, -expandValue, expandValue, expandValue);
816 if (expandValue > 0) {
817 newBounds = expandRect;
819 if (!maxBounds.
contains(newBounds)) {
820 newBounds = maxBounds;
824 if (expandValue > 0) {
825 expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
831 for (
int y = translatedSearchBounds.
top(); y <= translatedSearchBounds.
bottom(); y++)
833 for (
int x = translatedSearchBounds.
left(); x <= translatedSearchBounds.
right(); x++)
835 const int index = y * maxWidth + x;
836 if (!filledPixels[index])
840 (*replaceImage)->scanLine(x + left, y + top, fillColor);
844 delete[] filledPixels;
851bool* BitmapImage::floodFillPoints(
const BitmapImage* targetImage,
852 const QRect& searchBounds,
857 QRgb oldColor = targetImage->constScanLine(point.
x(), point.
y());
858 oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
867 bool spanLeft =
false;
868 bool spanRight =
false;
873 bool *filledPixels =
new bool[searchBounds.
height()*searchBounds.
width()]{};
876 while (!queue.
empty())
880 point.
setX(tempPoint.
x());
881 point.
setY(tempPoint.
y());
885 int xCoord = xTemp - searchBounds.
left();
886 int yCoord = point.
y() - searchBounds.
top();
888 if (filledPixels[yCoord*searchBounds.
width()+xCoord])
continue;
890 while (xTemp >= searchBounds.
left() &&
891 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data())) xTemp--;
894 spanLeft = spanRight =
false;
895 while (xTemp <= searchBounds.
right() &&
896 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data()))
900 if (!blitBounds.contains(floodPoint)) {
901 blitBounds.extend(floodPoint);
904 xCoord = xTemp - searchBounds.
left();
906 filledPixels[yCoord*searchBounds.
width()+xCoord] =
true;
908 if (!spanLeft && (point.
y() > searchBounds.
top()) &&
909 compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
913 else if (spanLeft && (point.
y() > searchBounds.
top()) &&
914 !
compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
918 if (!spanRight && point.
y() < searchBounds.
bottom() &&
919 compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
923 else if (spanRight && point.
y() < searchBounds.
bottom() &&
924 !
compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
933 newBounds = blitBounds;
955 const int maxWidth = maxBounds.
width();
956 const int length = maxBounds.
height() * maxBounds.
width();
958 int* manhattanPoints =
new int[length]{};
961 std::fill_n(manhattanPoints, length, searchBounds.
width()+searchBounds.
height());
963 for (
int y = searchBounds.
top(); y <= searchBounds.
bottom(); y++)
965 for (
int x = searchBounds.
left(); x <= searchBounds.
right(); x++)
967 const int index = y*maxWidth+x;
969 if (fillPixels[index]) {
970 manhattanPoints[index] = 0;
974 if (y > searchBounds.
top()) {
976 manhattanPoints[index] = qMin(manhattanPoints[index],
977 manhattanPoints[(y - 1) * maxWidth+x] + 1);
979 int distance = manhattanPoints[index];
980 if (distance <= expand) {
981 fillPixels[index] =
true;
984 if (x > searchBounds.
left()) {
986 manhattanPoints[index] = qMin(manhattanPoints[index],
987 manhattanPoints[y*maxWidth+(x - 1)] + 1);
989 int distance = manhattanPoints[index];
990 if (distance <= expand) {
991 fillPixels[index] =
true;
998 for (
int y = searchBounds.
bottom(); y >= searchBounds.
top(); y--)
1000 for (
int x = searchBounds.
right(); x >= searchBounds.
left(); x--)
1002 const int index = y*maxWidth+x;
1004 if (y + 1 < searchBounds.
bottom()) {
1005 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[(y + 1)*maxWidth+x] + 1);
1007 int distance = manhattanPoints[index];
1008 if (distance <= expand) {
1009 fillPixels[index] =
true;
1012 if (x + 1 < searchBounds.
right()) {
1013 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[y*maxWidth+(x + 1)] + 1);
1015 int distance = manhattanPoints[index];
1016 if (distance <= expand) {
1017 fillPixels[index] =
true;
1023 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