Changeset 279751 in webkit


Ignore:
Timestamp:
Jul 8, 2021 1:59:32 PM (13 months ago)
Author:
Wenson Hsieh
Message:

[Live Text] Selection is misaligned on some images on twitter.com
https://bugs.webkit.org/show_bug.cgi?id=227775
rdar://77142364

Reviewed by Tim Horton.

Source/WebCore:

On Twitter, image thumbnails consist of fully transparent image elements (opacity: 0;) that are overlaid over
div elements with a background image. The images for some of these transparent image elements sometimes don't
match the background image behind it (namely, the image is stretched to exactly fit the bounds of the
transparent image element in a way that does not preserve aspect ratio, whereas the background image is cropped
to cover the div). As such, Live Text in these image elements ends up misaligned against the background image
(which is actually visible to the user).

To address this, special case fully transparent images such that we take a snapshot of the page using the
absolute bounds of the transparent image and run OCR over this snapshot, instead of using the image data as-is.

Test: fast/images/text-recognition/image-overlay-in-transparent-image.html

  • html/HTMLElement.cpp:

(WebCore::HTMLElement::containerRectForTextRecognition):
(WebCore::HTMLElement::updateWithTextRecognitionResult):

  • html/HTMLElement.h:
  • page/Page.cpp:

(WebCore::Page::updateElementsWithTextRecognitionResults):

Source/WebKit:

See WebCore ChangeLog for more details.

  • WebProcess/WebCoreSupport/ShareableBitmapUtilities.cpp:

(WebKit::createShareableBitmap):

  • WebProcess/WebCoreSupport/ShareableBitmapUtilities.h:

(WebKit::createShareableBitmap):

Add an option to handle the scenario where the image renderer is fully transparent by falling back to a frame-
level snapshot, using the bounds of the image renderer.

  • WebProcess/WebPage/WebPage.cpp:

(WebKit::WebPage::requestTextRecognition):

  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::imagePositionInformation):
(WebKit::elementPositionInformation):

LayoutTests:

  • fast/images/text-recognition/image-overlay-in-transparent-image-expected.txt: Added.
  • fast/images/text-recognition/image-overlay-in-transparent-image.html: Added.
Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r279750 r279751  
     12021-07-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [Live Text] Selection is misaligned on some images on twitter.com
     4        https://bugs.webkit.org/show_bug.cgi?id=227775
     5        rdar://77142364
     6
     7        Reviewed by Tim Horton.
     8
     9        * fast/images/text-recognition/image-overlay-in-transparent-image-expected.txt: Added.
     10        * fast/images/text-recognition/image-overlay-in-transparent-image.html: Added.
     11
    1122021-07-08  Kate Cheney  <katherine_cheney@apple.com>
    213
  • trunk/Source/WebCore/ChangeLog

    r279750 r279751  
     12021-07-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [Live Text] Selection is misaligned on some images on twitter.com
     4        https://bugs.webkit.org/show_bug.cgi?id=227775
     5        rdar://77142364
     6
     7        Reviewed by Tim Horton.
     8
     9        On Twitter, image thumbnails consist of fully transparent image elements (`opacity: 0;`) that are overlaid over
     10        `div` elements with a background image. The images for some of these transparent image elements sometimes don't
     11        match the background image behind it (namely, the image is stretched to exactly fit the bounds of the
     12        transparent image element in a way that does not preserve aspect ratio, whereas the background image is cropped
     13        to cover the `div`). As such, Live Text in these image elements ends up misaligned against the background image
     14        (which is actually visible to the user).
     15
     16        To address this, special case fully transparent images such that we take a snapshot of the page using the
     17        absolute bounds of the transparent image and run OCR over this snapshot, instead of using the image data as-is.
     18
     19        Test: fast/images/text-recognition/image-overlay-in-transparent-image.html
     20
     21        * html/HTMLElement.cpp:
     22        (WebCore::HTMLElement::containerRectForTextRecognition):
     23        (WebCore::HTMLElement::updateWithTextRecognitionResult):
     24        * html/HTMLElement.h:
     25        * page/Page.cpp:
     26        (WebCore::Page::updateElementsWithTextRecognitionResults):
     27
    1282021-07-08  Kate Cheney  <katherine_cheney@apple.com>
    229
  • trunk/Source/WebCore/html/HTMLElement.cpp

    r279609 r279751  
    13411341#if ENABLE(IMAGE_ANALYSIS)
    13421342
     1343IntRect HTMLElement::containerRectForTextRecognition()
     1344{
     1345    auto* renderer = this->renderer();
     1346    if (!is<RenderImage>(renderer))
     1347        return { };
     1348
     1349    if (!renderer->opacity())
     1350        return { 0, 0, offsetWidth(), offsetHeight() };
     1351
     1352    return enclosingIntRect(downcast<RenderImage>(*renderer).replacedContentRect());
     1353}
     1354
    13431355void HTMLElement::updateWithTextRecognitionResult(const TextRecognitionResult& result, CacheTextRecognitionResults cacheTextRecognitionResults)
    13441356{
     
    14731485    downcast<RenderImage>(*renderer).setHasImageOverlay();
    14741486
    1475     auto containerRect = enclosingIntRect(downcast<RenderImage>(*renderer).replacedContentRect());
     1487    auto containerRect = containerRectForTextRecognition();
    14761488    auto convertToContainerCoordinates = [&](const FloatQuad& normalizedQuad) {
    14771489        auto quad = normalizedQuad;
  • trunk/Source/WebCore/html/HTMLElement.h

    r279108 r279751  
    140140
    141141#if ENABLE(IMAGE_ANALYSIS)
     142    IntRect containerRectForTextRecognition();
    142143    enum class CacheTextRecognitionResults : bool { No, Yes };
    143144    WEBCORE_EXPORT void updateWithTextRecognitionResult(const TextRecognitionResult&, CacheTextRecognitionResults = CacheTextRecognitionResults::Yes);
  • trunk/Source/WebCore/page/Page.cpp

    r279349 r279751  
    36313631            continue;
    36323632
    3633         auto newContainerRect = enclosingIntRect(downcast<RenderImage>(*renderer).replacedContentRect());
     3633        auto newContainerRect = protectedElement->containerRectForTextRecognition();
    36343634        if (containerRect == newContainerRect)
    36353635            continue;
  • trunk/Source/WebKit/ChangeLog

    r279750 r279751  
     12021-07-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [Live Text] Selection is misaligned on some images on twitter.com
     4        https://bugs.webkit.org/show_bug.cgi?id=227775
     5        rdar://77142364
     6
     7        Reviewed by Tim Horton.
     8
     9        See WebCore ChangeLog for more details.
     10
     11        * WebProcess/WebCoreSupport/ShareableBitmapUtilities.cpp:
     12        (WebKit::createShareableBitmap):
     13        * WebProcess/WebCoreSupport/ShareableBitmapUtilities.h:
     14        (WebKit::createShareableBitmap):
     15
     16        Add an option to handle the scenario where the image renderer is fully transparent by falling back to a frame-
     17        level snapshot, using the bounds of the image renderer.
     18
     19        * WebProcess/WebPage/WebPage.cpp:
     20        (WebKit::WebPage::requestTextRecognition):
     21        * WebProcess/WebPage/ios/WebPageIOS.mm:
     22        (WebKit::imagePositionInformation):
     23        (WebKit::elementPositionInformation):
     24
    1252021-07-08  Kate Cheney  <katherine_cheney@apple.com>
    226
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/ShareableBitmapUtilities.cpp

    r278253 r279751  
    2929#include "ShareableBitmap.h"
    3030#include <WebCore/CachedImage.h>
     31#include <WebCore/Frame.h>
     32#include <WebCore/FrameSnapshotting.h>
    3133#include <WebCore/GeometryUtilities.h>
    3234#include <WebCore/GraphicsContext.h>
     35#include <WebCore/ImageBuffer.h>
    3336#include <WebCore/IntSize.h>
    3437#include <WebCore/PlatformScreen.h>
     
    3841using namespace WebCore;
    3942
    40 RefPtr<ShareableBitmap> createShareableBitmap(RenderImage& renderImage, std::optional<FloatSize> screenSizeInPixels, AllowAnimatedImages allowAnimatedImages)
     43RefPtr<ShareableBitmap> createShareableBitmap(RenderImage& renderImage, CreateShareableBitmapFromImageOptions&& options)
    4144{
     45    Ref frame = renderImage.frame();
     46    auto colorSpaceForBitmap = screenColorSpace(frame->mainFrame().view());
     47    if (!renderImage.opacity() && options.useSnapshotForTransparentImages == UseSnapshotForTransparentImages::Yes) {
     48        auto snapshotRect = renderImage.absoluteBoundingBoxRect();
     49        if (snapshotRect.isEmpty())
     50            return { };
     51
     52        OptionSet<SnapshotFlags> snapshotFlags { SnapshotFlags::ExcludeSelectionHighlighting, SnapshotFlags::PaintEverythingExcludingSelection };
     53        auto imageBuffer = snapshotFrameRect(frame.get(), snapshotRect, { snapshotFlags, PixelFormat::BGRA8, DestinationColorSpace::SRGB() });
     54        if (!imageBuffer)
     55            return { };
     56
     57        auto snapshotImage = ImageBuffer::sinkIntoImage(WTFMove(imageBuffer), PreserveResolution::Yes);
     58        if (!snapshotImage)
     59            return { };
     60
     61        auto bitmap = ShareableBitmap::createShareable(snapshotRect.size(), { WTFMove(colorSpaceForBitmap) });
     62        if (!bitmap)
     63            return { };
     64
     65        auto context = bitmap->createGraphicsContext();
     66        if (!context)
     67            return { };
     68
     69        context->drawImage(*snapshotImage, { FloatPoint::zero(), snapshotRect.size() });
     70        return bitmap;
     71    }
     72
    4273    auto* cachedImage = renderImage.cachedImage();
    4374    if (!cachedImage || cachedImage->errorOccurred())
    44         return nullptr;
     75        return { };
    4576
    4677    auto* image = cachedImage->imageForRenderer(&renderImage);
    4778    if (!image || image->width() <= 1 || image->height() <= 1)
    48         return nullptr;
     79        return { };
    4980
    50     if (allowAnimatedImages == AllowAnimatedImages::No && image->isAnimated())
    51         return nullptr;
     81    if (options.allowAnimatedImages == AllowAnimatedImages::No && image->isAnimated())
     82        return { };
    5283
    5384    auto bitmapSize = cachedImage->imageSizeForRenderer(&renderImage);
    54     if (screenSizeInPixels) {
    55         auto scaledSize = largestRectWithAspectRatioInsideRect(bitmapSize.width() / bitmapSize.height(), { FloatPoint(), *screenSizeInPixels }).size();
     85    if (options.screenSizeInPixels) {
     86        auto scaledSize = largestRectWithAspectRatioInsideRect(bitmapSize.width() / bitmapSize.height(), { FloatPoint(), *options.screenSizeInPixels }).size();
    5687        bitmapSize = scaledSize.width() < bitmapSize.width() ? scaledSize : bitmapSize;
    5788    }
    5889
    5990    // FIXME: Only select ExtendedColor on images known to need wide gamut.
    60     auto sharedBitmap = ShareableBitmap::createShareable(IntSize(bitmapSize), { screenColorSpace(renderImage.frame().mainFrame().view()) });
     91    auto sharedBitmap = ShareableBitmap::createShareable(IntSize(bitmapSize), { WTFMove(colorSpaceForBitmap) });
    6192    if (!sharedBitmap)
    62         return nullptr;
     93        return { };
    6394
    6495    auto graphicsContext = sharedBitmap->createGraphicsContext();
    6596    if (!graphicsContext)
    66         return nullptr;
     97        return { };
    6798
    6899    graphicsContext->drawImage(*image, FloatRect(0, 0, bitmapSize.width(), bitmapSize.height()), { renderImage.imageOrientation() });
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/ShareableBitmapUtilities.h

    r278340 r279751  
    3737class ShareableBitmap;
    3838
     39enum class UseSnapshotForTransparentImages : bool { No, Yes };
    3940enum class AllowAnimatedImages : bool { No, Yes };
    40 RefPtr<ShareableBitmap> createShareableBitmap(WebCore::RenderImage&, std::optional<WebCore::FloatSize> screenSizeInPixels = std::nullopt, AllowAnimatedImages = AllowAnimatedImages::Yes);
     41
     42struct CreateShareableBitmapFromImageOptions {
     43    std::optional<WebCore::FloatSize> screenSizeInPixels;
     44    AllowAnimatedImages allowAnimatedImages { AllowAnimatedImages::Yes };
     45    UseSnapshotForTransparentImages useSnapshotForTransparentImages { UseSnapshotForTransparentImages::No };
     46};
     47
     48RefPtr<ShareableBitmap> createShareableBitmap(WebCore::RenderImage&, CreateShareableBitmapFromImageOptions&& = { });
    4149
    4250};
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r279750 r279751  
    74077407
    74087408    auto& renderImage = downcast<RenderImage>(*renderer);
    7409     auto bitmap = createShareableBitmap(renderImage, std::nullopt, AllowAnimatedImages::No);
     7409    auto bitmap = createShareableBitmap(renderImage, { std::nullopt, AllowAnimatedImages::No, UseSnapshotForTransparentImages::Yes });
    74107410    if (!bitmap) {
    74117411        if (completion)
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r279562 r279751  
    28242824
    28252825    if (request.includeSnapshot || request.includeImageData)
    2826         info.image = createShareableBitmap(renderImage, screenSize() * page.corePage()->deviceScaleFactor());
     2826        info.image = createShareableBitmap(renderImage, { screenSize() * page.corePage()->deviceScaleFactor(), AllowAnimatedImages::Yes, UseSnapshotForTransparentImages::Yes });
    28272827
    28282828    info.imageElementContext = page.contextForElement(element);
     
    28862886                    auto& [renderImage, image] = *rendererAndImage;
    28872887                    info.imageURL = element.document().completeURL(renderImage.cachedImage()->url().string());
    2888                     info.image = createShareableBitmap(renderImage, screenSize() * page.corePage()->deviceScaleFactor());
     2888                    info.image = createShareableBitmap(renderImage, { screenSize() * page.corePage()->deviceScaleFactor(), AllowAnimatedImages::Yes, UseSnapshotForTransparentImages::Yes });
    28892889                }
    28902890            }
Note: See TracChangeset for help on using the changeset viewer.