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())
757void BitmapImage::clear()
760 mBounds =
QRect(0, 0, 0, 0);
765QRgb BitmapImage::constScanLine(
int x,
int y)
const
767 QRgb result = QRgb();
769 result = *(
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(y - mBounds.
top())) + x - mBounds.
left());
774void BitmapImage::scanLine(
int x,
int y, QRgb color)
780 *(
reinterpret_cast<QRgb*
>(image()->
scanLine(y - mBounds.
top())) + x - mBounds.
left()) = color;
783void BitmapImage::clear(
QRect rectangle)
798bool BitmapImage::floodFill(
BitmapImage** replaceImage,
800 const QRect& cameraRect,
802 const QRgb& fillColor,
804 const int expandValue)
807 const QRect& fillBounds = targetImage->mBounds.
adjusted(-1, -1, 1, 1);
808 QRect maxBounds = cameraRect.
united(fillBounds).
adjusted(-expandValue, -expandValue, expandValue, expandValue);
809 const int maxWidth = maxBounds.
width(), left = maxBounds.
left(), top = maxBounds.
top();
812 tolerance =
static_cast<int>(qPow(tolerance, 2));
815 bool *filledPixels = floodFillPoints(targetImage, maxBounds, point, tolerance, newBounds);
820 const QRect& expandRect = newBounds.
adjusted(-expandValue, -expandValue, expandValue, expandValue);
821 if (expandValue > 0) {
822 newBounds = expandRect;
824 if (!maxBounds.
contains(newBounds)) {
825 newBounds = maxBounds;
829 if (expandValue > 0) {
830 expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
836 for (
int y = translatedSearchBounds.
top(); y <= translatedSearchBounds.
bottom(); y++)
838 for (
int x = translatedSearchBounds.
left(); x <= translatedSearchBounds.
right(); x++)
840 const int index = y * maxWidth + x;
841 if (!filledPixels[index])
845 (*replaceImage)->scanLine(x + left, y + top, fillColor);
849 delete[] filledPixels;
856bool* BitmapImage::floodFillPoints(
const BitmapImage* targetImage,
857 const QRect& searchBounds,
862 QRgb oldColor = targetImage->constScanLine(point.
x(), point.
y());
863 oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
872 bool spanLeft =
false;
873 bool spanRight =
false;
878 bool *filledPixels =
new bool[searchBounds.
height()*searchBounds.
width()]{};
881 while (!queue.
empty())
885 point.
setX(tempPoint.
x());
886 point.
setY(tempPoint.
y());
890 int xCoord = xTemp - searchBounds.
left();
891 int yCoord = point.
y() - searchBounds.
top();
893 if (filledPixels[yCoord*searchBounds.
width()+xCoord])
continue;
895 while (xTemp >= searchBounds.
left() &&
896 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data())) xTemp--;
899 spanLeft = spanRight =
false;
900 while (xTemp <= searchBounds.
right() &&
901 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data()))
905 if (!blitBounds.contains(floodPoint)) {
906 blitBounds.extend(floodPoint);
909 xCoord = xTemp - searchBounds.
left();
911 filledPixels[yCoord*searchBounds.
width()+xCoord] =
true;
913 if (!spanLeft && (point.
y() > searchBounds.
top()) &&
914 compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
918 else if (spanLeft && (point.
y() > searchBounds.
top()) &&
919 !
compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
923 if (!spanRight && point.
y() < searchBounds.
bottom() &&
924 compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
928 else if (spanRight && point.
y() < searchBounds.
bottom() &&
929 !
compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
938 newBounds = blitBounds;
960 const int maxWidth = maxBounds.
width();
961 const int length = maxBounds.
height() * maxBounds.
width();
963 int* manhattanPoints =
new int[length]{};
966 std::fill_n(manhattanPoints, length, searchBounds.
width()+searchBounds.
height());
968 for (
int y = searchBounds.
top(); y <= searchBounds.
bottom(); y++)
970 for (
int x = searchBounds.
left(); x <= searchBounds.
right(); x++)
972 const int index = y*maxWidth+x;
974 if (fillPixels[index]) {
975 manhattanPoints[index] = 0;
979 if (y > searchBounds.
top()) {
981 manhattanPoints[index] = qMin(manhattanPoints[index],
982 manhattanPoints[(y - 1) * maxWidth+x] + 1);
984 int distance = manhattanPoints[index];
985 if (distance <= expand) {
986 fillPixels[index] =
true;
989 if (x > searchBounds.
left()) {
991 manhattanPoints[index] = qMin(manhattanPoints[index],
992 manhattanPoints[y*maxWidth+(x - 1)] + 1);
994 int distance = manhattanPoints[index];
995 if (distance <= expand) {
996 fillPixels[index] =
true;
1003 for (
int y = searchBounds.
bottom(); y >= searchBounds.
top(); y--)
1005 for (
int x = searchBounds.
right(); x >= searchBounds.
left(); x--)
1007 const int index = y*maxWidth+x;
1009 if (y + 1 < searchBounds.
bottom()) {
1010 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[(y + 1)*maxWidth+x] + 1);
1012 int distance = manhattanPoints[index];
1013 if (distance <= expand) {
1014 fillPixels[index] =
true;
1017 if (x + 1 < searchBounds.
right()) {
1018 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[y*maxWidth+(x + 1)] + 1);
1020 int distance = manhattanPoints[index];
1021 if (distance <= expand) {
1022 fillPixels[index] =
true;
1028 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