generated at
Blinkでの画像のNaturalSizeの導出過程を追う

ChromiumのレンダリングエンジンBlinkでのHTMLImageElementの実装を読む

今回知りたいこと daiiz
画像の描画時にバイナリヘッダにあるサイズ情報を読んでいるか?
それとも一旦表示してみてpxサイズを取得するのか?
dpi awareなimg CustomElementをつくるを標準実装するとしたら?の考察に繋げたい

まとめ
BlinkのimgタグではHTTP Response Header Content-DPRが考慮される

以下、Chromiumを読んだメモ

HTMLImageElementが実装すべき関数が定義されているヘッダファイル
関数の目星をつけて同階層の .cc を読みに行くといい
ヘッダファイル便利
たぶんこれがimg要素の実体
currentSrc, SetSrc, Src() とかあるし多分合ってる
サイズ関係のgetter関数たち
.h
unsigned width(); unsigned height(); unsigned naturalWidth() const; unsigned naturalHeight() const; unsigned LayoutBoxWidth() const; unsigned LayoutBoxHeight() const;
何も知らなかったけどdpi awareなimg CustomElementをつくる#5c3961edadf4e70000bcc622で「ナチュラルサイズ」という表現使ってた。よい名称だったようだ daiiz
width, height は定数ではないということか
currentWidth みたいな意味合いかな?
naturalWidth, LayoutBoxWidth, width の順に読もう daiiz


cc
unsigned HTMLImageElement::naturalWidth() const { return DensityCorrectedIntrinsicDimensions().Width().ToUnsigned(); }
DensityCorrectedIntrinsicDimensionsを実行しているだけ
「密度補正された固有寸法」
密度とはDPIのこと?
Dimensionは寸法と訳すとしっくりくる! daiiz


LayoutSize型の定数を返す関数
cc
LayoutSize HTMLImageElement::DensityCorrectedIntrinsicDimensions() const { IntSize overridden_intrinsic_size = GetOverriddenIntrinsicSize(); if (!overridden_intrinsic_size.IsEmpty()) return LayoutSize(overridden_intrinsic_size); ImageResourceContent* image_resource = GetImageLoader().GetContent(); if (!image_resource || !image_resource->HasImage()) return LayoutSize(); float pixel_density = image_device_pixel_ratio_; /* なんだ?? */
このあたりが本質
cc
if (image_resource->HasDevicePixelRatioHeaderValue() && image_resource->DevicePixelRatioHeaderValue() > 0) pixel_density = 1 / image_resource->DevicePixelRatioHeaderValue();
あとはLayoutSizeを取得して、px_density補正しているだけ
cc
RespectImageOrientationEnum respect_image_orientation = LayoutObject::ShouldRespectImageOrientation(GetLayoutObject()); LayoutSize natural_size( image_resource->IntrinsicSize(respect_image_orientation)); natural_size.Scale(pixel_density); return natural_size; }
最終的にサイズを決定しているのはnatural_size
natural_size.Scale(pixel_density) することで論理サイズを決定してる
なんかわかった気がする daiizdaiizdaiiz

まずはDevicePixelRatioHeaderValueを読み解く

image_resource->DevicePixelRatioHeaderValue() の実装
cc
float ImageResourceContent::DevicePixelRatioHeaderValue() const { return device_pixel_ratio_header_value_; }
すでにどこかで device_pixel_ratio_header_value_ は確定しているらしい


device_pixel_ratio_header_value_ をセットするところ
なんと。HeaderとはHTTP Response Headerのことだったか!
画像のバイナリヘッダのことかと思っていた daiiz
http_names::kContentDPR というフィールドを読んでいる
cc
scoped_refptr<Image> ImageResourceContent::CreateImage(bool is_multipart) { String content_dpr_value = info_->GetResponse().HttpHeaderField(http_names::kContentDPR);
あとはいまは関係ない
content_dpr_valueを加工して device_pixel_ratio_header_value_ を確定しているだけ
cc
wtf_size_t comma = content_dpr_value.ReverseFind(','); if (comma != kNotFound && comma < content_dpr_value.length() - 1) { content_dpr_value = content_dpr_value.Substring(comma + 1); } device_pixel_ratio_header_value_ = content_dpr_value.ToFloat(&has_device_pixel_ratio_header_value_); if (!has_device_pixel_ratio_header_value_ || device_pixel_ratio_header_value_ <= 0.0) { device_pixel_ratio_header_value_ = 1.0; has_device_pixel_ratio_header_value_ = false; }
SVG画像か否かで実行されるCreateメソッドが違うのだな
コード中でBitmapImageを見たら、svg以外の画像という認識ができる
cc
if (info_->GetResponse().MimeType() == "image/svg+xml") return SVGImage::Create(this, is_multipart); return BitmapImage::Create(this, is_multipart); }


http_names::kContentDPR の定義
cc
const AtomicString& kContentDPR = reinterpret_cast<AtomicString*>(&names_storage)[15];

cc
struct NameEntry { const char* name; unsigned hash; unsigned char length; };
HTTP HeaderがNameEntry型で列挙されている
おもしろHeader探すときはググるよりもここを見るのが良さそうdaiiz
cc
... { "Cache-Control", 7757542, 13 }, { "Content-DPR", 8569724, 11 }, { "Content-Disposition", 362682, 19 }, ...
つまり
HTTP Response Headerに Content-DPR を付けて画像を配信すればよい?
Content-DPRを調べていく
なるほど
Content-DPRヘッダを付けてGyazo画像を返すAPIをGyakkyJSに実装した
いけた (Chromeで確認) daiizdaiizdaiiz


ここからは、LayoutSizeの求め方
IHDRなどを読んでいるのか?

cc
LayoutSize natural_size( image_resource->IntrinsicSize(respect_image_orientation)); natural_size.Scale(pixel_density); return natural_size; }

cc
IntSize ImageResourceContent::IntrinsicSize( RespectImageOrientationEnum should_respect_image_orientation) { if (!image_) return IntSize(); if (should_respect_image_orientation == kRespectImageOrientation && image_->IsBitmapImage()) return ToBitmapImage(image_.get())->SizeRespectingOrientation(); return image_->Size(); }
最後の SizeRespectingOrientation() Size() あたりを読めばいい?

SizeRespectingOrientation() を読んでいく
cc
IntSize BitmapImage::SizeRespectingOrientation() const { UpdateSize(); return size_respecting_orientation_; }

cc
void BitmapImage::UpdateSize() const { if (!size_available_ || have_size_ || !decoder_) return; size_ = decoder_->FrameSizeAtIndex(0); if (decoder_->OrientationAtIndex(0).UsesWidthAsHeight()) size_respecting_orientation_ = size_.TransposedSize(); else size_respecting_orientation_ = size_; have_size_ = true; }


cc
IntSize DeferredImageDecoder::FrameSizeAtIndex(size_t index) const { // FIXME: LocalFrame size is assumed to be uniform. This might not be true for // future supported codecs. return metadata_decoder_ ? metadata_decoder_->FrameSizeAtIndex(index) : size_; }
metadata_decoder_はすでにあると仮定して、 FrameSizeAtIndex(index) を読みに行く
この仮設間違えた気がする
ここではすでに求まっているsize_を返しているだけかも?
image_resourceがcreateされる箇所、SetSrcあたりを追ってみるべき

hr
詳細不明

SetSize

bool HeaderAvailable()

ParseSize()
HeaderAvailableの呼び出し元
IHDRを読んでいる気配がある
pHYsは登場してなさそう。もしここで読むように変更すると不都合でるのかな。
ここから逆に辿っていくといいかも


? (Chromium|Blink)で画像の(自然な大きさ|NaturalSize)がどのように(導出|決定)されるかの[調査]
? [Content-DPR]ヘッダーを発見した回