qpixmap.cpp

Absolute File Name:/home/qt/qt5_coco/qt5/qtbase/src/gui/image/qpixmap.cpp
Switch to Source codePreprocessed file
LineSourceCount
1-
2-
3-
4-
5-
6-
7-
8static bool qt_pixmap_thread_test()-
9{-
10 if (!(__builtin_expect(!!(!
__builtin_expe...nce()), false)Description
TRUEnever evaluated
FALSEnever evaluated
QCoreApplication::instance())()), false)
__builtin_expe...nce()), false)Description
TRUEnever evaluated
FALSEnever evaluated
) {
0
11 QMessageLogger(__FILE__, 6874, __PRETTY_FUNCTION__).fatal("QPixmap: Must construct a QGuiApplication before a QPixmap");-
12 return
never executed: return false;
false;
never executed: return false;
0
13 }-
14-
15 if ((static_cast<
(static_cast<Q...urrentThread()Description
TRUEnever evaluated
FALSEnever evaluated
QGuiApplication *>(QCoreApplication::instance()))->thread() != QThread::currentThread()
(static_cast<Q...urrentThread()Description
TRUEnever evaluated
FALSEnever evaluated
) {
0
16 bool fail = false;-
17 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps)
!QGuiApplicati...readedPixmaps)Description
TRUEnever evaluated
FALSEnever evaluated
) {
0
18 printf("Lighthouse plugin does not support threaded pixmaps!\n");-
19 fail = true;-
20 }
never executed: end of block
0
21 if (fail
failDescription
TRUEnever evaluated
FALSEnever evaluated
) {
0
22 QMessageLogger(__FILE__, 7985, __PRETTY_FUNCTION__).warning("QPixmap: It is not safe to use pixmaps outside the GUI thread");-
23 return
never executed: return false;
false;
never executed: return false;
0
24 }-
25 }
never executed: end of block
0
26 return
never executed: return true;
true;
never executed: return true;
0
27}-
28-
29void QPixmap::doInit(int w, int h, int type)-
30{-
31 if ((w > 0 && h > 0) || type == QPlatformPixmap::BitmapType)-
32 data = QPlatformPixmap::create(w, h, (QPlatformPixmap::PixelType) type);-
33 else-
34 data = 0;-
35}-
36-
37-
38-
39-
40-
41-
42-
43QPixmap::QPixmap()-
44 : QPaintDevice()-
45{-
46 (void) qt_pixmap_thread_test();-
47 doInit(0, 0, QPlatformPixmap::PixmapType);-
48}-
49QPixmap::QPixmap(int w, int h)-
50 : QPaintDevice()-
51{-
52 if (!qt_pixmap_thread_test())-
53 doInit(0, 0, QPlatformPixmap::PixmapType);-
54 else-
55 doInit(w, h, QPlatformPixmap::PixmapType);-
56}-
57QPixmap::QPixmap(const QSize &size)-
58 : QPaintDevice()-
59{-
60 if (!qt_pixmap_thread_test())-
61 doInit(0, 0, QPlatformPixmap::PixmapType);-
62 else-
63 doInit(size.width(), size.height(), QPlatformPixmap::PixmapType);-
64}-
65-
66-
67-
68-
69QPixmap::QPixmap(const QSize &s, int type)-
70{-
71 if (!qt_pixmap_thread_test())-
72 doInit(0, 0, static_cast<QPlatformPixmap::PixelType>(type));-
73 else-
74 doInit(s.width(), s.height(), static_cast<QPlatformPixmap::PixelType>(type));-
75}-
76-
77-
78-
79-
80QPixmap::QPixmap(QPlatformPixmap *d)-
81 : QPaintDevice(), data(d)-
82{-
83}-
84QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)-
85 : QPaintDevice()-
86{-
87 doInit(0, 0, QPlatformPixmap::PixmapType);-
88 if (!qt_pixmap_thread_test())-
89 return;-
90-
91 load(fileName, format, flags);-
92}-
93-
94-
95-
96-
97-
98-
99-
100QPixmap::QPixmap(const QPixmap &pixmap)-
101 : QPaintDevice()-
102{-
103 if (!qt_pixmap_thread_test()) {-
104 doInit(0, 0, QPlatformPixmap::PixmapType);-
105 return;-
106 }-
107 if (pixmap.paintingActive()) {-
108 pixmap.copy().swap(*this);-
109 } else {-
110 data = pixmap.data;-
111 }-
112}-
113QPixmap::QPixmap(const char * const xpm[])-
114 : QPaintDevice()-
115{-
116 doInit(0, 0, QPlatformPixmap::PixmapType);-
117 if (!xpm)-
118 return;-
119-
120 QImage image(xpm);-
121 if (!image.isNull()) {-
122 if (data && data->pixelType() == QPlatformPixmap::BitmapType)-
123 *this = QBitmap::fromImage(image);-
124 else-
125 *this = fromImage(image);-
126 }-
127}-
128-
129-
130-
131-
132-
133-
134-
135QPixmap::~QPixmap()-
136{-
137 ((!(!data || data->ref.load() >= 1)) ? qt_assert("!data || data->ref.load() >= 1",__FILE__,267273) : qt_noop());-
138}-
139-
140-
141-
142-
143int QPixmap::devType() const-
144{-
145 return QInternal::Pixmap;-
146}-
147QPixmap QPixmap::copy(const QRect &rect) const-
148{-
149 if (isNull())-
150 return QPixmap();-
151-
152 QRect r(0, 0, width(), height());-
153 if (!rect.isEmpty())-
154 r = r.intersected(rect);-
155-
156 QPlatformPixmap *d = data->createCompatiblePlatformPixmap();-
157 d->copy(data.data(), r);-
158 return QPixmap(d);-
159}-
160void QPixmap::scroll(int dx, int dy, const QRect &rect, QRegion *exposed)-
161{-
162 if (isNull() || (dx == 0 && dy == 0))-
163 return;-
164 QRect dest = rect & this->rect();-
165 QRect src = dest.translated(-dx, -dy) & dest;-
166 if (src.isEmpty()) {-
167 if (exposed)-
168 *exposed += dest;-
169 return;-
170 }-
171-
172 detach();-
173-
174 if (!data->scroll(dx, dy, src)) {-
175-
176 QPixmap pix = *this;-
177 QPainter painter(&pix);-
178 painter.setCompositionMode(QPainter::CompositionMode_Source);-
179 painter.drawPixmap(src.translated(dx, dy), *this, src);-
180 painter.end();-
181 *this = pix;-
182 }-
183-
184 if (exposed) {-
185 *exposed += dest;-
186 *exposed -= src.translated(dx, dy);-
187 }-
188}-
189QPixmap &QPixmap::operator=(const QPixmap &pixmap)-
190{-
191 if (paintingActive()) {-
192 QMessageLogger(__FILE__, 375381, __PRETTY_FUNCTION__).warning("QPixmap::operator=: Cannot assign to pixmap during painting");-
193 return *this;-
194 }-
195 if (pixmap.paintingActive()) {-
196 pixmap.copy().swap(*this);-
197 } else {-
198 data = pixmap.data;-
199 }-
200 return *this;-
201}-
202QPixmap::operator QVariant() const-
203{-
204 return QVariant(QVariant::Pixmap, this);-
205}-
206QImage QPixmap::toImage() const-
207{-
208 if (isNull())-
209 return QImage();-
210-
211 return data->toImage();-
212}-
213QTransform QPixmap::trueMatrix(const QTransform &m, int w, int h)-
214{-
215 return QImage::trueMatrix(m, w, h);-
216}-
217QMatrix QPixmap::trueMatrix(const QMatrix &m, int w, int h)-
218{-
219 return trueMatrix(QTransform(m), w, h).toAffine();-
220}-
221bool QPixmap::isQBitmap() const-
222{-
223 return data && data->type == QPlatformPixmap::BitmapType;-
224}-
225bool QPixmap::isNull() const-
226{-
227 return !data || data->isNull();-
228}-
229int QPixmap::width() const-
230{-
231 return data ? data->width() : 0;-
232}-
233int QPixmap::height() const-
234{-
235 return data ? data->height() : 0;-
236}-
237QSize QPixmap::size() const-
238{-
239 return data ? QSize(data->width(), data->height()) : QSize(0, 0);-
240}-
241QRect QPixmap::rect() const-
242{-
243 return data ? QRect(0, 0, data->width(), data->height()) : QRect();-
244}-
245int QPixmap::depth() const-
246{-
247 return data ? data->depth() : 0;-
248}-
249void QPixmap::setMask(const QBitmap &mask)-
250{-
251 if (paintingActive()) {-
252 QMessageLogger(__FILE__, 585591, __PRETTY_FUNCTION__).warning("QPixmap::setMask: Cannot set mask while pixmap is being painted on");-
253 return;-
254 }-
255-
256 if (!mask.isNull() && mask.size() != size()) {-
257 QMessageLogger(__FILE__, 590596, __PRETTY_FUNCTION__).warning("QPixmap::setMask() mask size differs from pixmap size");-
258 return;-
259 }-
260-
261 if (isNull())-
262 return;-
263-
264 if (static_cast<const QPixmap &>(mask).data == data)-
265 return;-
266-
267 detach();-
268-
269 QImage image = data->toImage();-
270 if (mask.size().isEmpty()) {-
271 if (image.depth() != 1) {-
272 image = image.convertToFormat(QImage::Format_RGB32);-
273 }-
274 } else {-
275 const int w = image.width();-
276 const int h = image.height();-
277-
278 switch (image.depth()) {-
279 case 1: {-
280 const QImage imageMask = mask.toImage().convertToFormat(image.format());-
281 for (int y = 0; y < h; ++y) {-
282 const uchar *mscan = imageMask.scanLine(y);-
283 uchar *tscan = image.scanLine(y);-
284 int bytesPerLine = image.bytesPerLine();-
285 for (int i = 0; i < bytesPerLine; ++i)-
286 tscan[i] &= mscan[i];-
287 }-
288 break;-
289 }-
290 default: {-
291 const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);-
292 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);-
293 for (int y = 0; y < h; ++y) {-
294 const uchar *mscan = imageMask.scanLine(y);-
295 QRgb *tscan = (QRgb *)image.scanLine(y);-
296 for (int x = 0; x < w; ++x) {-
297 if (!(mscan[x>>3] & (1 << (x&7))))-
298 tscan[x] = 0;-
299 }-
300 }-
301 break;-
302 }-
303 }-
304 }-
305 data->fromImage(image, Qt::AutoColor);-
306}-
307qreal QPixmap::devicePixelRatio() const-
308{-
309 if (!data)-
310 return qreal(1.0);-
311 return data->devicePixelRatio();-
312}-
313void QPixmap::setDevicePixelRatio(qreal scaleFactor)-
314{-
315 if (isNull())-
316 return;-
317-
318 if (scaleFactor == data->devicePixelRatio())-
319 return;-
320-
321 detach();-
322 data->setDevicePixelRatio(scaleFactor);-
323}-
324QBitmap QPixmap::createHeuristicMask(bool clipTight) const-
325{-
326 QBitmap m = QBitmap::fromImage(toImage().createHeuristicMask(clipTight));-
327 return m;-
328}-
329QBitmap QPixmap::createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const-
330{-
331 QImage image = toImage().convertToFormat(QImage::Format_ARGB32);-
332 return QBitmap::fromImage(image.createMaskFromColor(maskColor.rgba(), mode));-
333}-
334bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)-
335{-
336 if (!fileName.isEmpty()) {-
337-
338 QFileInfo info(fileName);-
339-
340-
341 if (info.completeSuffix().isEmpty() || info.exists()) {-
342-
343 QString key = QLatin1String("qt_pixmap")-
344 % info.absoluteFilePath()-
345 % HexString<uint>(info.lastModified().toTime_t())-
346 % HexString<quint64>(info.size())-
347 % HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType);-
348-
349 if (QPixmapCache::find(key, this))-
350 return true;-
351-
352 data = QPlatformPixmap::create(0, 0, data ? data->pixelType() : QPlatformPixmap::PixmapType);-
353-
354 if (data->fromFile(fileName, format, flags)) {-
355 QPixmapCache::insert(key, *this);-
356 return true;-
357 }-
358 }-
359 }-
360-
361 if (!isNull()) {-
362 if (isQBitmap())-
363 *this = QBitmap();-
364 else-
365 data.reset();-
366 }-
367 return false;-
368}-
369bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Qt::ImageConversionFlags flags)-
370{-
371 if (len == 0 || buf == 0) {-
372 data.reset();-
373 return false;-
374 }-
375-
376 data = QPlatformPixmap::create(0, 0, QPlatformPixmap::PixmapType);-
377-
378 if (data->fromData(buf, len, format, flags))-
379 return true;-
380-
381 data.reset();-
382 return false;-
383}-
384bool QPixmap::save(const QString &fileName, const char *format, int quality) const-
385{-
386 if (isNull())-
387 return false;-
388 QImageWriter writer(fileName, format);-
389 return doImageIO(&writer, quality);-
390}-
391bool QPixmap::save(QIODevice* device, const char* format, int quality) const-
392{-
393 if (isNull())-
394 return false;-
395 QImageWriter writer(device, format);-
396 return doImageIO(&writer, quality);-
397}-
398-
399-
400-
401bool QPixmap::doImageIO(QImageWriter *writer, int quality) const-
402{-
403 if (quality > 100 || quality < -1)-
404 QMessageLogger(__FILE__, 891897, __PRETTY_FUNCTION__).warning("QPixmap::save: quality out of range [-1,100]");-
405 if (quality >= 0)-
406 writer->setQuality(qMin(quality,100));-
407 return writer->write(toImage());-
408}-
409void QPixmap::fill(const QPaintDevice *device, const QPoint &p)-
410{-
411 (void)device;-
412 (void)p;-
413 QMessageLogger(__FILE__, 908914, __PRETTY_FUNCTION__).warning("this function is deprecated, ignored");-
414}-
415void QPixmap::fill(const QColor &color)-
416{-
417 if (isNull())-
418 return;-
419-
420-
421-
422 if (paintingActive() && (color.alpha() != 255) && !hasAlphaChannel()) {-
423 QMessageLogger(__FILE__, 937943, __PRETTY_FUNCTION__).warning("QPixmap::fill: Cannot fill while pixmap is being painted on");-
424 return;-
425 }-
426-
427 if (data->ref.load() == 1) {-
428-
429-
430 detach();-
431 } else {-
432-
433-
434 QPlatformPixmap *d = data->createCompatiblePlatformPixmap();-
435 d->resize(data->width(), data->height());-
436 data = d;-
437 }-
438 data->fill(color);-
439}-
440qint64 QPixmap::cacheKey() const-
441{-
442 if (isNull())-
443 return 0;-
444-
445 ((!(data)) ? qt_assert("data",__FILE__,982988) : qt_noop());-
446 return data->cacheKey();-
447}-
448QPixmap QPixmap::grabWidget(QObject *widget, const QRect &rectangle)-
449{-
450 QPixmap pixmap;-
451 QMessageLogger(__FILE__, 10091015, __PRETTY_FUNCTION__).warning("QPixmap::grabWidget is deprecated, use QWidget::grab() instead");-
452 if (!widget)-
453 return pixmap;-
454 QMetaObject::invokeMethod(widget, "grab", Qt::DirectConnection,-
455 QReturnArgument<QPixmap >("QPixmap", pixmap),-
456 QArgument<QRect >("QRect", rectangle));-
457 return pixmap;-
458}-
459QDataStream &operator<<(QDataStream &stream, const QPixmap &pixmap)-
460{-
461 return stream << pixmap.toImage();-
462}-
463QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap)-
464{-
465 QImage image;-
466 stream >> image;-
467-
468 if (image.isNull()) {-
469 pixmap = QPixmap();-
470 } else if (image.depth() == 1) {-
471 pixmap = QBitmap::fromImage(image);-
472 } else {-
473 pixmap = QPixmap::fromImage(image);-
474 }-
475 return stream;-
476}-
477-
478-
479-
480-
481-
482-
483-
484bool QPixmap::isDetached() const-
485{-
486 return data && data->ref.load() == 1;-
487}-
488bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags flags)-
489{-
490 detach();-
491 if (image.isNull() || !data)-
492 *this = QPixmap::fromImage(image, flags);-
493 else-
494 data->fromImage(image, flags);-
495 return !isNull();-
496}-
497QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const-
498{-
499 if (isNull()) {-
500 QMessageLogger(__FILE__, 11531159, __PRETTY_FUNCTION__).warning("QPixmap::scaled: Pixmap is a null pixmap");-
501 return QPixmap();-
502 }-
503 if (s.isEmpty())-
504 return QPixmap();-
505-
506 QSize newSize = size();-
507 newSize.scale(s, aspectMode);-
508 newSize.rwidth() = qMax(newSize.width(), 1);-
509 newSize.rheight() = qMax(newSize.height(), 1);-
510 if (newSize == size())-
511 return *this;-
512-
513 QTransform wm = QTransform::fromScale((qreal)newSize.width() / width(),-
514 (qreal)newSize.height() / height());-
515 QPixmap pix = transformed(wm, mode);-
516 return pix;-
517}-
518QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const-
519{-
520 if (isNull()) {-
521 QMessageLogger(__FILE__, 11891195, __PRETTY_FUNCTION__).warning("QPixmap::scaleWidth: Pixmap is a null pixmap");-
522 return copy();-
523 }-
524 if (w <= 0)-
525 return QPixmap();-
526-
527 qreal factor = (qreal) w / width();-
528 QTransform wm = QTransform::fromScale(factor, factor);-
529 return transformed(wm, mode);-
530}-
531QPixmap QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const-
532{-
533 if (isNull()) {-
534 QMessageLogger(__FILE__, 12171223, __PRETTY_FUNCTION__).warning("QPixmap::scaleHeight: Pixmap is a null pixmap");-
535 return copy();-
536 }-
537 if (h <= 0)-
538 return QPixmap();-
539-
540 qreal factor = (qreal) h / height();-
541 QTransform wm = QTransform::fromScale(factor, factor);-
542 return transformed(wm, mode);-
543}-
544QPixmap QPixmap::transformed(const QTransform &transform,-
545 Qt::TransformationMode mode) const-
546{-
547 if (isNull() || transform.type() <= QTransform::TxTranslate)-
548 return *this;-
549-
550 return data->transformed(transform, mode);-
551}-
552-
553-
554-
555-
556-
557-
558-
559QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) const-
560{-
561 return transformed(QTransform(matrix), mode);-
562}-
563bool QPixmap::hasAlpha() const-
564{-
565 return data && data->hasAlphaChannel();-
566}-
567-
568-
569-
570-
571-
572-
573-
574bool QPixmap::hasAlphaChannel() const-
575{-
576 return data && data->hasAlphaChannel();-
577}-
578-
579-
580-
581-
582int QPixmap::metric(PaintDeviceMetric metric) const-
583{-
584 return data ? data->metric(metric) : 0;-
585}-
586-
587-
588-
589-
590QPaintEngine *QPixmap::paintEngine() const-
591{-
592 return data ? data->paintEngine() : 0;-
593}-
594QBitmap QPixmap::mask() const-
595{-
596 if (!data || !hasAlphaChannel())-
597 return QBitmap();-
598-
599 const QImage img = toImage();-
600 bool shouldConvert = (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_ARGB32_Premultiplied);-
601 const QImage image = (shouldConvert ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) : img);-
602 const int w = image.width();-
603 const int h = image.height();-
604-
605 QImage mask(w, h, QImage::Format_MonoLSB);-
606 if (mask.isNull())-
607 return QBitmap();-
608-
609 mask.setColorCount(2);-
610 mask.setColor(0, QColor(Qt::color0).rgba());-
611 mask.setColor(1, QColor(Qt::color1).rgba());-
612-
613 const int bpl = mask.bytesPerLine();-
614-
615 for (int y = 0; y < h; ++y) {-
616 const QRgb *src = reinterpret_cast<const QRgb*>(image.scanLine(y));-
617 uchar *dest = mask.scanLine(y);-
618 memset(dest, 0, bpl);-
619 for (int x = 0; x < w; ++x) {-
620 if (qAlpha(*src) > 0)-
621 dest[x >> 3] |= (1 << (x & 7));-
622 ++src;-
623 }-
624 }-
625-
626 return QBitmap::fromImage(mask);-
627}-
628int QPixmap::defaultDepth()-
629{-
630 return QGuiApplication::primaryScreen()->depth();-
631}-
632void QPixmap::detach()-
633{-
634 if (!data)-
635 return;-
636-
637-
638-
639 QPlatformPixmap *pd = handle();-
640 QPlatformPixmap::ClassId id = pd->classId();-
641 if (id == QPlatformPixmap::RasterClass) {-
642 QRasterPlatformPixmap *rasterData = static_cast<QRasterPlatformPixmap*>(pd);-
643 rasterData->image.detach();-
644 }-
645-
646 if (data->is_cached && data->ref.load() == 1)-
647 QImagePixmapCleanupHooks::executePlatformPixmapModificationHooks(data.data());-
648-
649 if (data->ref.load() != 1) {-
650 *this = copy();-
651 }-
652 ++data->detach_no;-
653}-
654QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)-
655{-
656 if (image.isNull())-
657 return QPixmap();-
658-
659 QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType));-
660 data->fromImage(image, flags);-
661 return QPixmap(data.take());-
662}-
663QPixmap QPixmap::fromImageInPlace(QImage &image, Qt::ImageConversionFlags flags)-
664{-
665 if (image.isNull())-
666 return QPixmap();-
667-
668 QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType));-
669 data->fromImageInPlace(image, flags);-
670 return QPixmap(data.take());-
671}-
672QPixmap QPixmap::fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags)-
673{-
674 QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType));-
675 data->fromImageReader(imageReader, flags);-
676 return QPixmap(data.take());-
677}-
678QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)-
679{-
680 QMessageLogger(__FILE__, 17021708, __PRETTY_FUNCTION__).warning("this function is deprecated, use QScreen::grabWindow() instead."-
681 " Defaulting to primary screen.");-
682 return QGuiApplication::primaryScreen()->grabWindow(window, x, y, w, h);-
683}-
684-
685-
686-
687-
688QPlatformPixmap* QPixmap::handle() const-
689{-
690 return data.data();-
691}-
692-
693-
694QDebug operator<<(QDebug dbg, const QPixmap &r)-
695{-
696 QDebugStateSaver saver(dbg);-
697 dbg.resetFormat();-
698 dbg.nospace();-
699 dbg << "QPixmap(";-
700 if (r.isNull()) {-
701 dbg << "null";-
702 } else {-
703 dbg << r.size() << ",depth=" << r.depth()-
704 << ",devicePixelRatio=" << r.devicePixelRatio()-
705 << ",cacheKey=" << showbase << hex << r.cacheKey() << dec << noshowbase;-
706 }-
707 dbg << ')';-
708 return dbg;-
709}-
710-
Switch to Source codePreprocessed file

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9