Blinkでの画像のNaturalSizeの導出過程を追う
dpi awareなimg CustomElementをつくる話題の関連調査
ChromiumのレンダリングエンジンBlinkでのHTMLImageElementの実装を読む
今回知りたいこと daiiz.icon
画像の描画時にバイナリヘッダにあるサイズ情報を読んでいるか?
それとも一旦表示してみてpxサイズを取得するのか?
dpi awareなimg CustomElementをつくるを標準実装するとしたら?の考察に繋げたい
まとめ
BlinkのimgタグではHTTP Response Header Content-DPRが考慮される
以下、Chromiumを読んだメモ
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_image_element.h
HTMLImageElementが実装すべき関数が定義されているヘッダファイル
関数の目星をつけて同階層の.ccを読みに行くといい
ヘッダファイル便利
たぶんこれがimg要素の実体
currentSrc, SetSrc, Src() とかあるし多分合ってる
サイズ関係のgetter関数たち
code:.h
unsigned width();
unsigned height();
unsigned naturalWidth() const;
unsigned naturalHeight() const;
unsigned LayoutBoxWidth() const;
unsigned LayoutBoxHeight() const;
何も知らなかったけどdpi awareなimg CustomElementをつくる#5c3961edadf4e70000bcc622で「ナチュラルサイズ」という表現使ってた。よい名称だったようだ daiiz.icon
width, height は定数ではないということか
currentWidth みたいな意味合いかな?
naturalWidth, LayoutBoxWidth, width の順に読もう daiiz.icon
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_image_element.cc
code:cc
unsigned HTMLImageElement::naturalWidth() const {
return DensityCorrectedIntrinsicDimensions().Width().ToUnsigned();
}
DensityCorrectedIntrinsicDimensionsを実行しているだけ
「密度補正された固有寸法」
密度とはDPIのこと?
Dimensionは寸法と訳すとしっくりくる! daiiz.icon
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_image_element.cc
LayoutSize型の定数を返す関数
code: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_; /* なんだ?? */
このあたりが本質
dpi awareなimg CustomElementをつくる#5c3961edadf4e70000bcc620と同じことやってる
code:cc
if (image_resource->HasDevicePixelRatioHeaderValue() &&
image_resource->DevicePixelRatioHeaderValue() > 0)
pixel_density = 1 / image_resource->DevicePixelRatioHeaderValue();
あとはLayoutSizeを取得して、px_density補正しているだけ
code: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)することで論理サイズを決定してる
なんかわかった気がする daiiz.icon*3
まずはDevicePixelRatioHeaderValueを読み解く
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/loader/resource/image_resource_content.cc?type=cs&sq=package:chromium&g=0&l=617
image_resource->DevicePixelRatioHeaderValue() の実装
code:cc
float ImageResourceContent::DevicePixelRatioHeaderValue() const {
return device_pixel_ratio_header_value_;
}
すでにどこかでdevice_pixel_ratio_header_value_は確定しているらしい
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/loader/resource/image_resource_content.cc?type=cs&sq=package:chromium&g=0&l=301
device_pixel_ratio_header_value_をセットするところ
なんと。HeaderとはHTTP Response Headerのことだったか!
画像のバイナリヘッダのことかと思っていた daiiz.icon
http_names::kContentDPRというフィールドを読んでいる
code: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_を確定しているだけ
code: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以外の画像という認識ができる
code:cc
if (info_->GetResponse().MimeType() == "image/svg+xml")
return SVGImage::Create(this, is_multipart);
return BitmapImage::Create(this, is_multipart);
}
https://cs.chromium.org/chromium/src/out/Debug/gen/third_party/blink/renderer/platform/network/http_names.cc?type=cs&sq=package:chromium&g=0&l=36
http_names::kContentDPRの定義
code:cc
const AtomicString& kContentDPR = reinterpret_cast<AtomicString*>(&names_storage)15;
https://cs.chromium.org/chromium/src/out/Debug/gen/third_party/blink/renderer/platform/network/http_names.cc?type=cs&sq=package:chromium&g=0&l=113
code:cc
struct NameEntry {
const char* name;
unsigned hash;
unsigned char length;
};
HTTP HeaderがNameEntry型で列挙されている
おもしろHeader探すときはググるよりもここを見るのが良さそうdaiiz.icon
code: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に実装した
https://gyakky.herokuapp.com/gyazo/content-dpr/a196681066f27ea6e6247805c908839e#.png
いけた (Chromeで確認) daiiz.icon*3
ここからは、LayoutSizeの求め方
IHDRなどを読んでいるのか?
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_image_element.cc?type=cs&sq=package:chromium&g=0&l=539
code:cc
LayoutSize natural_size(
image_resource->IntrinsicSize(respect_image_orientation));
natural_size.Scale(pixel_density);
return natural_size;
}
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/loader/resource/image_resource_content.cc?type=cs&sq=package:chromium&g=0&l=246
code: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()あたりを読めばいい?
https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/bitmap_image.cc?type=cs&sq=package:chromium&g=0&l=149
SizeRespectingOrientation()を読んでいく
code:cc
IntSize BitmapImage::SizeRespectingOrientation() const {
UpdateSize();
return size_respecting_orientation_;
}
code: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;
}
https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc?type=cs&sq=package:chromium&g=0&l=204
code: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あたりを追ってみるべき
/icons/hr.icon
詳細不明
https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc?type=cs&sq=package:chromium&g=0&l=256
SetSize
https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc?type=cs&sq=package:chromium&g=0&l=264
bool HeaderAvailable()
https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc?type=cs&sq=package:chromium&g=0&l=512
ParseSize()
HeaderAvailableの呼び出し元
IHDRを読んでいる気配がある
pHYsは登場してなさそう。もしここで読むように変更すると不都合でるのかな。
ここから逆に辿っていくといいかも
#Chromiumを読む
? (Chromium|Blink)で画像の(自然な大きさ|NaturalSize)がどのように(導出|決定)されるかの調査
? Content-DPRヘッダーを発見した回