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;
803 QRect maxBounds = cameraRect.
united(fillBounds).
adjusted(-expandValue, -expandValue, expandValue, expandValue);
804 const int maxWidth = maxBounds.
width(), left = maxBounds.
left(), top = maxBounds.
top();
813 tolerance =
static_cast<int>(qPow(tolerance, 2));
816 bool shouldFillBorder =
false;
817 bool *filledPixels = floodFillPoints(targetImage, fillBounds, maxBounds, point, tolerance, newBounds, shouldFillBorder);
821 if (shouldFillBorder)
823 for (
int y = 0; y < maxBounds.
height(); y++)
825 for (
int x = 0; x < maxBounds.
width(); x++)
827 if(!translatedSearchBounds.
contains(x, y))
829 filledPixels[y*maxWidth+x] =
true;
833 newBounds = maxBounds;
837 const QRect& expandRect = newBounds.
adjusted(-expandValue, -expandValue, expandValue, expandValue);
838 if (expandValue > 0) {
839 newBounds = expandRect;
841 if (!maxBounds.
contains(newBounds)) {
842 newBounds = maxBounds;
846 if (expandValue > 0) {
847 expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
853 for (
int y = translatedSearchBounds.
top(); y <= translatedSearchBounds.
bottom(); y++)
855 for (
int x = translatedSearchBounds.
left(); x <= translatedSearchBounds.
right(); x++)
857 const int index = y * maxWidth + x;
858 if (!filledPixels[index])
862 (*replaceImage)->scanLine(x + left, y + top, fillColor);
866 delete[] filledPixels;
873bool* BitmapImage::floodFillPoints(
const BitmapImage* targetImage,
875 const QRect& maxBounds,
881 QRgb oldColor = targetImage->constScanLine(point.
x(), point.
y());
882 oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
893 bool spanLeft =
false;
894 bool spanRight =
false;
899 point = searchBounds.
topLeft();
905 bool *filledPixels =
new bool[maxBounds.
height()*maxBounds.
width()]{};
908 bool checkOutside =
false;
911 while (!queue.
empty())
915 point.
setX(tempPoint.
x());
916 point.
setY(tempPoint.
y());
920 int xCoord = xTemp - maxBounds.
left();
921 int yCoord = point.
y() - maxBounds.
top();
924 if (!borderBounds.
contains(point)) {
928 if (filledPixels[yCoord*maxBounds.
width()+xCoord])
continue;
930 while (xTemp >= searchBounds.
left() &&
931 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data())) xTemp--;
934 spanLeft = spanRight =
false;
935 while (xTemp <= searchBounds.
right() &&
936 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data()))
940 if (!blitBounds.contains(floodPoint)) {
941 blitBounds.extend(floodPoint);
944 xCoord = xTemp - maxBounds.
left();
946 filledPixels[yCoord*maxBounds.
width()+xCoord] =
true;
948 if (!spanLeft && (point.
y() > searchBounds.
top()) &&
949 compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
953 else if (spanLeft && (point.
y() > searchBounds.
top()) &&
954 !
compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
958 if (!spanRight && point.
y() < searchBounds.
bottom() &&
959 compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
963 else if (spanRight && point.
y() < searchBounds.
bottom() &&
964 !
compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
973 fillBorder = checkOutside &&
compareColor(qRgba(0,0,0,0), oldColor, tolerance, cache.data());
975 newBounds = blitBounds;
997 const int maxWidth = maxBounds.
width();
998 const int length = maxBounds.
height() * maxBounds.
width();
1000 int* manhattanPoints =
new int[length]{};
1003 std::fill_n(manhattanPoints, length, searchBounds.
width()+searchBounds.
height());
1005 for (
int y = searchBounds.
top(); y <= searchBounds.
bottom(); y++)
1007 for (
int x = searchBounds.
left(); x <= searchBounds.
right(); x++)
1009 const int index = y*maxWidth+x;
1011 if (fillPixels[index]) {
1012 manhattanPoints[index] = 0;
1016 if (y > searchBounds.
top()) {
1018 manhattanPoints[index] = qMin(manhattanPoints[index],
1019 manhattanPoints[(y - 1) * maxWidth+x] + 1);
1021 int distance = manhattanPoints[index];
1022 if (distance <= expand) {
1023 fillPixels[index] =
true;
1026 if (x > searchBounds.
left()) {
1028 manhattanPoints[index] = qMin(manhattanPoints[index],
1029 manhattanPoints[y*maxWidth+(x - 1)] + 1);
1031 int distance = manhattanPoints[index];
1032 if (distance <= expand) {
1033 fillPixels[index] =
true;
1040 for (
int y = searchBounds.
bottom(); y >= searchBounds.
top(); y--)
1042 for (
int x = searchBounds.
right(); x >= searchBounds.
left(); x--)
1044 const int index = y*maxWidth+x;
1046 if (y + 1 < searchBounds.
bottom()) {
1047 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[(y + 1)*maxWidth+x] + 1);
1049 int distance = manhattanPoints[index];
1050 if (distance <= expand) {
1051 fillPixels[index] =
true;
1054 if (x + 1 < searchBounds.
right()) {
1055 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[y*maxWidth+(x + 1)] + 1);
1057 int distance = manhattanPoints[index];
1058 if (distance <= expand) {
1059 fillPixels[index] =
true;
1065 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