17#include "bitmapimage.h"
23#include <QPainterPath>
28BitmapImage::BitmapImage()
36 mEnableAutoCrop = a.mEnableAutoCrop;
37 mOpacity = a.mOpacity;
41BitmapImage::BitmapImage(
const QRect& rectangle,
const QColor& color)
49BitmapImage::BitmapImage(
const QPoint& topLeft,
const QImage& image)
56BitmapImage::BitmapImage(
const QPoint& topLeft,
const QString& path)
66BitmapImage::~BitmapImage()
70void BitmapImage::setImage(
QImage* img)
86 KeyFrame::operator=(a);
89 mOpacity = a.mOpacity;
100 const bool validKeyFrame = !fileName().
isEmpty();
101 if (validKeyFrame && !isLoaded())
106 Q_ASSERT(finfo.isAbsolute());
110 .
arg(finfo.canonicalPath())
111 .
arg(finfo.completeBaseName())
112 .
arg(uniqueString(12))
113 .
arg(finfo.suffix());
114 b->setFileName(newFileName);
118 qDebug() <<
"COPY>" << fileName();
123void BitmapImage::loadFile()
125 if (!fileName().isEmpty() && !isLoaded())
133void BitmapImage::unloadFile()
135 if (isModified() ==
false)
141bool BitmapImage::isLoaded()
const
146quint64 BitmapImage::memoryUsage()
150 return imageSize(mImage);
155void BitmapImage::paintImage(
QPainter& painter)
167QImage* BitmapImage::image()
190 if(bitmapImage->width() <= 0 || bitmapImage->height() <= 0)
197 QImage* image2 = bitmapImage->image();
207void BitmapImage::moveTopLeft(
QPoint point)
214void BitmapImage::transform(
QRect newBoundaries,
bool smoothTransform)
216 mBounds = newBoundaries;
225 painter.
drawImage(newBoundaries, *image());
234 Q_ASSERT(!selection.
isEmpty());
246 transformedImage = selectedPart.image()->
transformed(transform);
251BitmapImage BitmapImage::transformed(
QRect newBoundaries,
bool smoothTransform)
254 QPainter painter(transformedImage.image());
257 painter.
drawImage(newBoundaries, *image());
259 return transformedImage;
272 if (mBounds == newBoundaries)
return;
283 mBounds = newBoundaries;
289void BitmapImage::extend(
const QPoint &p)
297void BitmapImage::extend(
QRect rectangle)
310 if (!newImage.isNull())
317 mBounds = newBoundaries;
366 newBoundaries = mBounds;
375 newBoundaries = mBounds;
382 newBoundaries = mBounds.
united(sourceBounds);
404 if (!mEnableAutoCrop)
return;
406 if (mImage.
isNull())
return;
408 Q_ASSERT(mBounds.
size() == mImage.
size());
414 const int width = mImage.
width();
418 int relBottom = mBounds.
height() - 1;
422 while (isEmpty && relTop <= relBottom)
425 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop));
426 for (
int col = 0; col < width; col++)
430 if (qAlpha(*cursor) != 0)
450 while (isEmpty && relBottom >= relTop)
453 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relBottom));
454 for (
int col = 0; col < width; col++)
458 if(qAlpha(*cursor) != 0)
478 int relRight = mBounds.
width()-1;
481 isEmpty = (relBottom >= relTop);
482 while (isEmpty && relBottom >= relTop && relLeft <= relRight)
485 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop)) + relLeft;
491 for (
int row = relTop; row <= relBottom; row++)
495 if(qAlpha(*cursor) != 0)
515 isEmpty = (relBottom >= relTop);
516 while (isEmpty && relRight >= relLeft)
519 const QRgb* cursor =
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(relTop)) + relRight;
525 for (
int row = relTop; row <= relBottom; row++)
529 if(qAlpha(*cursor) != 0)
558QRgb BitmapImage::pixel(
int x,
int y)
560 return pixel(
QPoint(x, y));
563QRgb BitmapImage::pixel(
QPoint p)
565 QRgb result = qRgba(0, 0, 0, 0);
571void BitmapImage::setPixel(
int x,
int y, QRgb color)
573 setPixel(
QPoint(x, y), color);
576void BitmapImage::setPixel(
QPoint p, QRgb color)
586void BitmapImage::fillNonAlphaPixels(
const QRgb color)
588 if (mBounds.
isEmpty()) {
return; }
596 int width = 2 + pen.
width();
598 if (!image()->isNull())
612 int width = pen.
width();
635 int width = pen.
width();
660 int width = pen.
width();
665 if (!image()->isNull())
708 bool b = mImage.
save(filename);
709 return (b) ? Status::OK : Status::FAIL;
712 if (bounds().isEmpty())
718 return (b) ? Status::OK : Status::FAIL;
725void BitmapImage::clear()
728 mBounds =
QRect(0, 0, 0, 0);
733QRgb BitmapImage::constScanLine(
int x,
int y)
const
735 QRgb result = QRgb();
737 result = *(
reinterpret_cast<const QRgb*
>(mImage.
constScanLine(y - mBounds.
top())) + x - mBounds.
left());
742void BitmapImage::scanLine(
int x,
int y, QRgb color)
748 *(
reinterpret_cast<QRgb*
>(image()->
scanLine(y - mBounds.
top())) + x - mBounds.
left()) = color;
751void BitmapImage::clear(
QRect rectangle)
766bool BitmapImage::floodFill(
BitmapImage** replaceImage,
768 const QRect& cameraRect,
770 const QRgb& fillColor,
772 const int expandValue)
775 const QRect& fillBounds = targetImage->mBounds;
776 QRect maxBounds = cameraRect.
united(fillBounds).
adjusted(-expandValue, -expandValue, expandValue, expandValue);
777 const int maxWidth = maxBounds.
width(), left = maxBounds.
left(), top = maxBounds.
top();
786 tolerance =
static_cast<int>(qPow(tolerance, 2));
789 bool shouldFillBorder =
false;
790 bool *filledPixels = floodFillPoints(targetImage, fillBounds, maxBounds, point, tolerance, newBounds, shouldFillBorder);
794 if (shouldFillBorder)
796 for (
int y = 0; y < maxBounds.
height(); y++)
798 for (
int x = 0; x < maxBounds.
width(); x++)
800 if(!translatedSearchBounds.
contains(x, y))
802 filledPixels[y*maxWidth+x] =
true;
806 newBounds = maxBounds;
810 const QRect& expandRect = newBounds.
adjusted(-expandValue, -expandValue, expandValue, expandValue);
811 if (expandValue > 0) {
812 newBounds = expandRect;
814 if (!maxBounds.
contains(newBounds)) {
815 newBounds = maxBounds;
819 if (expandValue > 0) {
820 expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
826 for (
int y = translatedSearchBounds.
top(); y <= translatedSearchBounds.
bottom(); y++)
828 for (
int x = translatedSearchBounds.
left(); x <= translatedSearchBounds.
right(); x++)
830 const int index = y * maxWidth + x;
831 if (!filledPixels[index])
835 (*replaceImage)->scanLine(x + left, y + top, fillColor);
839 delete[] filledPixels;
846bool* BitmapImage::floodFillPoints(
const BitmapImage* targetImage,
848 const QRect& maxBounds,
854 QRgb oldColor = targetImage->constScanLine(point.
x(), point.
y());
855 oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
866 bool spanLeft =
false;
867 bool spanRight =
false;
872 point = searchBounds.
topLeft();
878 bool *filledPixels =
new bool[maxBounds.
height()*maxBounds.
width()]{};
881 bool checkOutside =
false;
884 while (!queue.
empty())
888 point.
setX(tempPoint.
x());
889 point.
setY(tempPoint.
y());
893 int xCoord = xTemp - maxBounds.
left();
894 int yCoord = point.
y() - maxBounds.
top();
897 if (!borderBounds.
contains(point)) {
901 if (filledPixels[yCoord*maxBounds.
width()+xCoord])
continue;
903 while (xTemp >= searchBounds.
left() &&
904 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data())) xTemp--;
907 spanLeft = spanRight =
false;
908 while (xTemp <= searchBounds.
right() &&
909 compareColor(targetImage->constScanLine(xTemp, point.
y()), oldColor, tolerance, cache.data()))
913 if (!blitBounds.contains(floodPoint)) {
914 blitBounds.extend(floodPoint);
917 xCoord = xTemp - maxBounds.
left();
919 filledPixels[yCoord*maxBounds.
width()+xCoord] =
true;
921 if (!spanLeft && (point.
y() > searchBounds.
top()) &&
922 compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
926 else if (spanLeft && (point.
y() > searchBounds.
top()) &&
927 !
compareColor(targetImage->constScanLine(xTemp, point.
y() - 1), oldColor, tolerance, cache.data())) {
931 if (!spanRight && point.
y() < searchBounds.
bottom() &&
932 compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
936 else if (spanRight && point.
y() < searchBounds.
bottom() &&
937 !
compareColor(targetImage->constScanLine(xTemp, point.
y() + 1), oldColor, tolerance, cache.data())) {
946 fillBorder = checkOutside &&
compareColor(qRgba(0,0,0,0), oldColor, tolerance, cache.data());
948 newBounds = blitBounds;
970 const int maxWidth = maxBounds.
width();
971 const int length = maxBounds.
height() * maxBounds.
width();
973 int* manhattanPoints =
new int[length]{};
976 std::fill_n(manhattanPoints, length, searchBounds.
width()+searchBounds.
height());
978 for (
int y = searchBounds.
top(); y <= searchBounds.
bottom(); y++)
980 for (
int x = searchBounds.
left(); x <= searchBounds.
right(); x++)
982 const int index = y*maxWidth+x;
984 if (fillPixels[index]) {
985 manhattanPoints[index] = 0;
989 if (y > searchBounds.
top()) {
991 manhattanPoints[index] = qMin(manhattanPoints[index],
992 manhattanPoints[(y - 1) * maxWidth+x] + 1);
994 int distance = manhattanPoints[index];
995 if (distance <= expand) {
996 fillPixels[index] =
true;
999 if (x > searchBounds.
left()) {
1001 manhattanPoints[index] = qMin(manhattanPoints[index],
1002 manhattanPoints[y*maxWidth+(x - 1)] + 1);
1004 int distance = manhattanPoints[index];
1005 if (distance <= expand) {
1006 fillPixels[index] =
true;
1013 for (
int y = searchBounds.
bottom(); y >= searchBounds.
top(); y--)
1015 for (
int x = searchBounds.
right(); x >= searchBounds.
left(); x--)
1017 const int index = y*maxWidth+x;
1019 if (y + 1 < searchBounds.
bottom()) {
1020 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[(y + 1)*maxWidth+x] + 1);
1022 int distance = manhattanPoints[index];
1023 if (distance <= expand) {
1024 fillPixels[index] =
true;
1027 if (x + 1 < searchBounds.
right()) {
1028 manhattanPoints[index] = qMin(manhattanPoints[index], manhattanPoints[y*maxWidth+(x + 1)] + 1);
1030 int distance = manhattanPoints[index];
1031 if (distance <= expand) {
1032 fillPixels[index] =
true;
1038 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 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